aiobungie

A Pythonic async/await wrapper for interacting with the Bungie API.

Base client.

Example
import aiobungie

client = aiobungie.Client('YOUR_API_KEY')

# Search for Destiny2 users.
async def main() -> None:
    async with client.rest:
        users = await client.search_users('Crit')

        # Iterate over the users and take the first 5 results.
        for user in users.take(5):
            print(f'{user.name} ({user.code})')

            # Iterate through the users memberships.
            for membership in user.memberships:
                print(membership.type, membership.id)

client.run(main()) # or asyncio.run(main())

Single RESTClient instance.

The difference between base client and the REST clients:

  • No Hight-Level concepts.
  • All returned data are pure JSON objects from the API.
  • No object creation.
Example
import aiobungie

async def main() -> None:
    # Using `async with` context manager to close the session properly.
    async with aiobungie.RESTClient("TOKEN") as rest:
        payload = await rest.fetch_player('Fate怒', 4275)

        for membership in payload:
            print(membership['membershipId'], membership['iconPath'])

import asyncio
asyncio.run(main())

REST client pool.

A REST client pool allows you to acquire multiple RESTClient instances that shares the same connection.

Example
import aiobungie
import asyncio

pool = aiobungie.RESTPool("token")

async def func1() -> None:
    async with pool.acquire() as instance:
        tokens = await instance.fetch_oauth2_tokens('code')
        pool.metadata['tokens'] = tokens

# Other instance may access the tokens from pool since its shared.

async def func2() -> None:
    async with pool.acquire() as instance:
        tokens = pool.metadata['tokens']
        tokens = await instance.refresh_access_token(tokens.refresh_token)

async def main() -> None:
    await asyncio.gather(func1(), func2())

asyncio.run(main())

Should you use the base client or the REST client? This returns to you. For an example if you're building a website.

You can use python as a REST API in the backend with the RESTClient since all returned object are JSON objects. Which gives you the freedom to deserialize it and implement your own logic in the front-end.

Or of you're building a Discord bot for an example or something simple. The base client is the way to go.

  1# MIT License
  2#
  3# Copyright (c) 2020 - Present nxtlo
  4#
  5# Permission is hereby granted, free of charge, to any person obtaining a copy
  6# of this software and associated documentation files (the "Software"), to deal
  7# in the Software without restriction, including without limitation the rights
  8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9# copies of the Software, and to permit persons to whom the Software is
 10# furnished to do so, subject to the following conditions:
 11#
 12# The above copyright notice and this permission notice shall be included in all
 13# copies or substantial portions of the Software.
 14#
 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21# SOFTWARE.
 22
 23"""A Pythonic `async`/`await` wrapper for interacting with the Bungie API.
 24
 25Base client.
 26
 27Example
 28-------
 29```py
 30import aiobungie
 31
 32client = aiobungie.Client('YOUR_API_KEY')
 33
 34# Search for Destiny2 users.
 35async def main() -> None:
 36    async with client.rest:
 37        users = await client.search_users('Crit')
 38
 39        # Iterate over the users and take the first 5 results.
 40        for user in users.take(5):
 41            print(f'{user.name} ({user.code})')
 42
 43            # Iterate through the users memberships.
 44            for membership in user.memberships:
 45                print(membership.type, membership.id)
 46
 47client.run(main()) # or asyncio.run(main())
 48```
 49
 50Single RESTClient instance.
 51
 52The difference between base client and the REST clients:
 53
 54* No Hight-Level concepts.
 55* All returned data are pure JSON objects from the API.
 56* No object creation.
 57
 58Example
 59-------
 60```py
 61import aiobungie
 62
 63async def main() -> None:
 64    # Using `async with` context manager to close the session properly.
 65    async with aiobungie.RESTClient("TOKEN") as rest:
 66        payload = await rest.fetch_player('Fate怒', 4275)
 67
 68        for membership in payload:
 69            print(membership['membershipId'], membership['iconPath'])
 70
 71import asyncio
 72asyncio.run(main())
 73```
 74
 75REST client pool.
 76
 77A REST client pool allows you to acquire multiple `RESTClient` instances that shares the same connection.
 78
 79Example
 80-------
 81```py
 82import aiobungie
 83import asyncio
 84
 85pool = aiobungie.RESTPool("token")
 86
 87async def func1() -> None:
 88    async with pool.acquire() as instance:
 89        tokens = await instance.fetch_oauth2_tokens('code')
 90        pool.metadata['tokens'] = tokens
 91
 92# Other instance may access the tokens from pool since its shared.
 93
 94async def func2() -> None:
 95    async with pool.acquire() as instance:
 96        tokens = pool.metadata['tokens']
 97        tokens = await instance.refresh_access_token(tokens.refresh_token)
 98
 99async def main() -> None:
100    await asyncio.gather(func1(), func2())
101
102asyncio.run(main())
103```
104
105Should you use the base client or the REST client?
106This returns to you. For an example if you're building a website.
107
108You can use python as a REST API in the backend with the RESTClient since all returned object are JSON objects.
109Which gives you the freedom to deserialize it and implement your own logic in the front-end.
110
111Or of you're building a Discord bot for an example or something simple. The base client is the way to go.
112"""
113
114
115from __future__ import annotations
116
117from aiobungie import builders
118from aiobungie import crates
119from aiobungie import interfaces
120from aiobungie import traits
121from aiobungie import typedefs
122from aiobungie import url
123from aiobungie.client import Client
124from aiobungie.error import *
125from aiobungie.internal import iterators
126from aiobungie.internal.assets import Image
127from aiobungie.internal.enums import *
128from aiobungie.internal.factory import Factory
129from aiobungie.internal.iterators import *
130from aiobungie.rest import *
131from aiobungie.undefined import UNDEFINED
132from aiobungie.undefined import UndefinedOr
133from aiobungie.undefined import UndefinedType
134
135from .metadata import __about__
136from .metadata import __author__
137from .metadata import __docs__
138from .metadata import __email__
139from .metadata import __license__
140from .metadata import __url__
141from .metadata import __version__
142
143# Alias for crate for backwards compatibility.
144crate = crates
145
146# Activity enums
147from .crates.activity import Difficulty
148
149# Components enums
150from .crates.components import ComponentFields
151from .crates.components import ComponentPrivacy
152
153# Entity enums
154from .crates.entity import GatingScope
155from .crates.entity import ObjectiveUIStyle
156from .crates.entity import ValueUIStyle
157
158# Fireteam enums.
159from .crates.fireteams import FireteamActivity
160from .crates.fireteams import FireteamDate
161from .crates.fireteams import FireteamLanguage
162from .crates.fireteams import FireteamPlatform
163
164# Records enums
165from .crates.records import RecordState
166
167__all__ = [mod for mod in dir() if not mod.startswith("_")]  # type: ignore
@attrs.define(auto_exc=True)
class AiobungieError(builtins.RuntimeError):
74@attrs.define(auto_exc=True)
75class AiobungieError(RuntimeError):
76    """Base class that all other exceptions inherit from."""

Base class that all other exceptions inherit from.

AiobungieError()
2def __init__(self, ):
3    BaseException.__init__(self, )

Method generated by attrs for class AiobungieError.

Inherited Members
builtins.BaseException
with_traceback
@typing.final
class AmmoType(builtins.int, aiobungie.Enum):
643@typing.final
644class AmmoType(int, Enum):
645    """AN enum for Detyiny 2 ammo types."""
646
647    NONE = 0
648    PRIMARY = 1
649    SPECIAL = 2
650    HEAVY = 3

AN enum for Detyiny 2 ammo types.

NONE = <AmmoType.NONE: 0>
PRIMARY = <AmmoType.PRIMARY: 1>
SPECIAL = <AmmoType.SPECIAL: 2>
HEAVY = <AmmoType.HEAVY: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class BadRequest(aiobungie.HTTPError):
164@attrs.define(auto_exc=True)
165class BadRequest(HTTPError):
166    """An exception raised when requesting a resource with the provided data is wrong."""
167
168    url: typing.Optional[typedefs.StrOrURL]
169    """The URL/endpoint caused this error."""
170
171    body: typing.Any
172    """The response body."""
173
174    headers: multidict.CIMultiDictProxy[str]
175    """The response headers."""
176
177    http_status: http.HTTPStatus = attrs.field(
178        default=http.HTTPStatus.BAD_REQUEST, init=False
179    )

An exception raised when requesting a resource with the provided data is wrong.

BadRequest( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str])
2def __init__(self, message, url, body, headers):
3    self.message = message
4    self.url = url
5    self.body = body
6    self.headers = headers
7    self.http_status = attr_dict['http_status'].default
8    BaseException.__init__(self, self.message,self.url,self.body,self.headers)

Method generated by attrs for class BadRequest.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

http_status: http.HTTPStatus

The response status.

Inherited Members
HTTPError
message
builtins.BaseException
with_traceback
@typing.final
class ClanMemberType(builtins.int, aiobungie.Enum):
698@typing.final
699class ClanMemberType(int, Enum):
700    """An enum for bungie clan member types."""
701
702    NONE = 0
703    BEGINNER = 1
704    MEMBER = 2
705    ADMIN = 3
706    ACTING_FOUNDER = 4
707    FOUNDER = 5

An enum for bungie clan member types.

NONE = <ClanMemberType.NONE: 0>
BEGINNER = <ClanMemberType.BEGINNER: 1>
MEMBER = <ClanMemberType.MEMBER: 2>
ADMIN = <ClanMemberType.ADMIN: 3>
ACTING_FOUNDER = <ClanMemberType.ACTING_FOUNDER: 4>
FOUNDER = <ClanMemberType.FOUNDER: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Class(builtins.int, aiobungie.Enum):
474@typing.final
475class Class(int, Enum):
476    """An Enum for Destiny character classes."""
477
478    TITAN = 0
479    HUNTER = 1
480    WARLOCK = 2
481    UNKNOWN = 3

An Enum for Destiny character classes.

TITAN = <Class.TITAN: 0>
HUNTER = <Class.HUNTER: 1>
WARLOCK = <Class.WARLOCK: 2>
UNKNOWN = <Class.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Client(aiobungie.traits.ClientApp):
  60class Client(traits.ClientApp):
  61    """Standard Bungie API client application.
  62
  63    This client deserialize the REST JSON responses using `aiobungie.internal.factory.Factory`
  64    and returns `aiobungie.crates` Python object implementations of the responses.
  65
  66    A `aiobungie.RESTClient` REST client can also be used alone for low-level concepts.
  67
  68    Example
  69    -------
  70    ```py
  71    import aiobungie
  72
  73    client = aiobungie.Client('...')
  74
  75    async def main():
  76        async with client.rest:
  77            user = await client.fetch_current_user_memberships('...')
  78            print(user)
  79    ```
  80
  81    Parameters
  82    -----------
  83    token: `str`
  84        Your Bungie's API key or Token from the developer's portal.
  85
  86    Other Parameters
  87    ----------------
  88    max_retries : `int`
  89        The max retries number to retry if the request hit a `5xx` status code.
  90    max_ratelimit_retries : `int`
  91        The max retries number to retry if the request hit a `429` status code. Defaults to `3`.
  92    client_secret : `str | None`
  93        An optional application client secret,
  94        This is only needed if you're fetching OAuth2 tokens with this client.
  95    client_id : `int | None`
  96        An optional application client id,
  97        This is only needed if you're fetching OAuth2 tokens with this client.
  98    """
  99
 100    __slots__ = ("_rest", "_factory")
 101
 102    def __init__(
 103        self,
 104        token: str,
 105        /,
 106        client_secret: typing.Optional[str] = None,
 107        client_id: typing.Optional[int] = None,
 108        *,
 109        max_retries: int = 4,
 110        max_ratelimit_retries: int = 3,
 111    ) -> None:
 112
 113        self._rest = rest_.RESTClient(
 114            token,
 115            client_secret,
 116            client_id,
 117            max_retries=max_retries,
 118            max_ratelimit_retries=max_ratelimit_retries,
 119        )
 120
 121        self._factory = factory_.Factory(self)
 122
 123    @property
 124    def factory(self) -> factory_.Factory:
 125        return self._factory
 126
 127    @property
 128    def rest(self) -> interfaces.RESTInterface:
 129        return self._rest
 130
 131    @property
 132    def request(self) -> Client:
 133        return self
 134
 135    @property
 136    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 137        return self._rest.metadata
 138
 139    def run(
 140        self, future: collections.Coroutine[typing.Any, None, None], debug: bool = False
 141    ) -> None:
 142        loop: typing.Final[asyncio.AbstractEventLoop] = helpers.get_or_make_loop()
 143        try:
 144            if not loop.is_running():
 145                loop.set_debug(debug)
 146                loop.run_until_complete(future)
 147
 148        except Exception as exc:
 149            raise RuntimeError(f"Failed to run {future.__qualname__}") from exc
 150
 151        except KeyboardInterrupt:
 152            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")
 153            return
 154
 155    # * User methods.
 156
 157    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
 158        """Fetch and return a user object of the bungie net user associated with account.
 159
 160        .. warning::
 161            This method requires OAuth2 scope and a Bearer access token.
 162
 163        Parameters
 164        ----------
 165        access_token : `str`
 166            A valid Bearer access token for the authorization.
 167
 168        Returns
 169        -------
 170        `aiobungie.crates.user.User`
 171            A user object includes the Destiny memberships and Bungie.net user.
 172        """
 173        resp = await self.rest.fetch_current_user_memberships(access_token)
 174
 175        return self.factory.deserialize_user(resp)
 176
 177    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
 178        """Fetch a Bungie user by their BungieNet id.
 179
 180        .. note::
 181            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
 182            for other memberships.
 183
 184        Parameters
 185        ----------
 186        id: `int`
 187            The user id.
 188
 189        Returns
 190        -------
 191        `aiobungie.crates.user.BungieUser`
 192            A Bungie user.
 193
 194        Raises
 195        ------
 196        `aiobungie.error.NotFound`
 197            The user was not found.
 198        """
 199        payload = await self.rest.fetch_bungie_user(id)
 200
 201        return self.factory.deserialize_bungie_user(payload)
 202
 203    async def search_users(
 204        self, name: str, /
 205    ) -> iterators.Iterator[user.SearchableDestinyUser]:
 206        """Search for players and return all players that matches the same name.
 207
 208        Parameters
 209        ----------
 210        name : `buildins.str`
 211            The user name.
 212
 213        Returns
 214        -------
 215        `aiobungie.iterators.Iterator[aiobungie.crates.DestinyMembership]`
 216            A sequence of destiny memberships.
 217        """
 218        payload = await self.rest.search_users(name)
 219
 220        return iterators.Iterator(
 221            [
 222                self.factory.deserialize_searched_user(user)
 223                for user in payload["searchResults"]
 224            ]
 225        )
 226
 227    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
 228        """Fetch all available user themes.
 229
 230        Returns
 231        -------
 232        `collections.Sequence[aiobungie.crates.user.UserThemes]`
 233            A sequence of user themes.
 234        """
 235        data = await self.rest.fetch_user_themes()
 236
 237        return self.factory.deserialize_user_themes(data)
 238
 239    async def fetch_hard_types(
 240        self,
 241        credential: int,
 242        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
 243        /,
 244    ) -> user.HardLinkedMembership:
 245        """Gets any hard linked membership given a credential.
 246        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
 247        Cross Save aware.
 248
 249        Parameters
 250        ----------
 251        credential: `int`
 252            A valid SteamID64
 253        type: `aiobungie.CredentialType`
 254            The credential type. This must not be changed
 255            Since its only credential that works "currently"
 256
 257        Returns
 258        -------
 259        `aiobungie.crates.user.HardLinkedMembership`
 260            Information about the hard linked data.
 261        """
 262
 263        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
 264
 265        return user.HardLinkedMembership(
 266            id=int(payload["membershipId"]),
 267            type=enums.MembershipType(payload["membershipType"]),
 268            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
 269        )
 270
 271    async def fetch_membership_from_id(
 272        self,
 273        id: int,
 274        /,
 275        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 276    ) -> user.User:
 277        """Fetch Bungie user's memberships from their id.
 278
 279        Notes
 280        -----
 281        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
 282        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
 283        see `aiobungie.crates.user.DestinyMembership` for more details.
 284        * If you only want the bungie user. Consider using `Client.fetch_user` method.
 285
 286        Parameters
 287        ----------
 288        id : `int`
 289            The user's id.
 290        type : `aiobungie.MembershipType`
 291            The user's membership type.
 292
 293        Returns
 294        -------
 295        `aiobungie.crates.User`
 296            A Bungie user with their membership types.
 297
 298        Raises
 299        ------
 300        aiobungie.NotFound
 301            The requested user was not found.
 302        """
 303        payload = await self.rest.fetch_membership_from_id(id, type)
 304
 305        return self.factory.deserialize_user(payload)
 306
 307    async def fetch_user_credentials(
 308        self, access_token: str, membership_id: int, /
 309    ) -> collections.Sequence[user.UserCredentials]:
 310        """Fetch an array of credential types attached to the requested account.
 311
 312        .. note::
 313            This method require OAuth2 Bearer access token.
 314
 315        Parameters
 316        ----------
 317        access_token : `str`
 318            The bearer access token associated with the bungie account.
 319        membership_id : `int`
 320            The id of the membership to return.
 321
 322        Returns
 323        -------
 324        `collections.Sequence[aiobungie.crates.UserCredentials]`
 325            A sequence of the attached user credentials.
 326
 327        Raises
 328        ------
 329        `aiobungie.Unauthorized`
 330            The access token was wrong or no access token passed.
 331        """
 332        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
 333
 334        return self.factory.deserialize_user_credentials(resp)
 335
 336    # * Destiny 2.
 337
 338    async def fetch_profile(
 339        self,
 340        member_id: int,
 341        type: typedefs.IntAnd[enums.MembershipType],
 342        components: list[enums.ComponentType],
 343        auth: typing.Optional[str] = None,
 344    ) -> components.Component:
 345        """
 346        Fetch a bungie profile passing components to the request.
 347
 348        Parameters
 349        ----------
 350        member_id: `int`
 351            The member's id.
 352        type: `aiobungie.MembershipType`
 353            A valid membership type.
 354        components : `list[aiobungie.ComponentType]`
 355            List of profile components to collect and return.
 356
 357        Other Parameters
 358        ----------------
 359        auth : `typing.Optional[str]`
 360            A Bearer access_token to make the request with.
 361            This is optional and limited to components that only requires an Authorization token.
 362
 363        Returns
 364        --------
 365        `aiobungie.crates.Component`
 366            A Destiny 2 player profile with its components.
 367            Only passed components will be available if they exists. Otherwise they will be `None`
 368
 369        Raises
 370        ------
 371        `aiobungie.MembershipTypeError`
 372            The provided membership type was invalid.
 373        """
 374        data = await self.rest.fetch_profile(member_id, type, components, auth)
 375        return self.factory.deserialize_components(data)
 376
 377    async def fetch_linked_profiles(
 378        self,
 379        member_id: int,
 380        member_type: typedefs.IntAnd[enums.MembershipType],
 381        /,
 382        *,
 383        all: bool = False,
 384    ) -> profile.LinkedProfile:
 385        """Returns a summary information about all profiles linked to the requested member.
 386
 387        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
 388
 389        .. note::
 390            It will only return linked accounts whose linkages you are allowed to view.
 391
 392        Parameters
 393        ----------
 394        member_id : `int`
 395            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
 396        member_type : `aiobungie.MembershipType`
 397            The type for the membership whose linked Destiny account you want to return.
 398
 399        Other Parameters
 400        ----------------
 401        all : `bool`
 402            If provided and set to `True`, All memberships regardless
 403            of whether they're obscured by overrides will be returned,
 404
 405            If provided and set to `False`, Only available memberships will be returned.
 406            The default for this is `False`.
 407
 408        Returns
 409        -------
 410        `aiobungie.crates.profile.LinkedProfile`
 411            A linked profile object.
 412        """
 413        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
 414
 415        return self.factory.deserialize_linked_profiles(resp)
 416
 417    async def fetch_player(
 418        self,
 419        name: str,
 420        code: int,
 421        /,
 422        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
 423    ) -> collections.Sequence[user.DestinyMembership]:
 424        """Fetch a Destiny 2 player's memberships.
 425
 426        Parameters
 427        -----------
 428        name: `str`
 429            The unique Bungie player name.
 430        code : `int`
 431            The unique Bungie display name code.
 432        type: `aiobungie.internal.enums.MembershipType`
 433            The player's membership type, e,g. XBOX, STEAM, PSN
 434
 435        Returns
 436        --------
 437        `collections.Sequence[aiobungie.crates.DestinyMembership]`
 438            A sequence of the found Destiny 2 player memberships.
 439            An empty sequence will be returned if no one found.
 440
 441        Raises
 442        ------
 443        `aiobungie.MembershipTypeError`
 444            The provided membership type was invalid.
 445        """
 446        resp = await self.rest.fetch_player(name, code, type)
 447
 448        return self.factory.deserialize_destiny_memberships(resp)
 449
 450    async def fetch_character(
 451        self,
 452        member_id: int,
 453        membership_type: typedefs.IntAnd[enums.MembershipType],
 454        character_id: int,
 455        components: list[enums.ComponentType],
 456        auth: typing.Optional[str] = None,
 457    ) -> components.CharacterComponent:
 458        """Fetch a Destiny 2 character.
 459
 460        Parameters
 461        ----------
 462        member_id: `int`
 463            A valid bungie member id.
 464        character_id: `int`
 465            The Destiny character id to retrieve.
 466        membership_type: `aiobungie.internal.enums.MembershipType`
 467            The member's membership type.
 468        components: `list[aiobungie.ComponentType]`
 469            Multiple arguments of character components to collect and return.
 470
 471        Other Parameters
 472        ----------------
 473        auth : `typing.Optional[str]`
 474            A Bearer access_token to make the request with.
 475            This is optional and limited to components that only requires an Authorization token.
 476
 477        Returns
 478        -------
 479        `aiobungie.crates.CharacterComponent`
 480            A Bungie character component.
 481
 482        `aiobungie.MembershipTypeError`
 483            The provided membership type was invalid.
 484        """
 485        resp = await self.rest.fetch_character(
 486            member_id, membership_type, character_id, components, auth
 487        )
 488
 489        return self.factory.deserialize_character_component(resp)
 490
 491    async def fetch_unique_weapon_history(
 492        self,
 493        membership_id: int,
 494        character_id: int,
 495        membership_type: typedefs.IntAnd[enums.MembershipType],
 496    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
 497        """Fetch details about unique weapon usage for a character. Includes all exotics.
 498
 499        Parameters
 500        ----------
 501        membership_id : `int`
 502            The Destiny user membership id.
 503        character_id : `int`
 504            The character id to retrieve.
 505        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 506            The Destiny user's membership type.
 507
 508        Returns
 509        -------
 510        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
 511            A sequence of the weapon's extended values.
 512        """
 513        resp = await self._rest.fetch_unique_weapon_history(
 514            membership_id, character_id, membership_type
 515        )
 516
 517        return [
 518            self._factory.deserialize_extended_weapon_values(weapon)
 519            for weapon in resp["weapons"]
 520        ]
 521
 522    # * Destiny 2 Activities.
 523
 524    async def fetch_activities(
 525        self,
 526        member_id: int,
 527        character_id: int,
 528        mode: typedefs.IntAnd[enums.GameMode],
 529        *,
 530        membership_type: typedefs.IntAnd[
 531            enums.MembershipType
 532        ] = enums.MembershipType.ALL,
 533        page: int = 0,
 534        limit: int = 250,
 535    ) -> iterators.Iterator[activity.Activity]:
 536        """Fetch a Destiny 2 activity for the specified character id.
 537
 538        Parameters
 539        ----------
 540        member_id: `int`
 541            The user id that starts with `4611`.
 542        character_id: `int`
 543            The id of the character to retrieve the activities for.
 544        mode: `aiobungie.typedefs.IntAnd[aiobungie.internal.enums.GameMode]`
 545            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
 546
 547        Other Parameters
 548        ----------------
 549        membership_type: `aiobungie.internal.enums.MembershipType`
 550            The Member ship type, if nothing was passed than it will return all.
 551        page: int
 552            The page number. Default is `0`
 553        limit: int
 554            Limit the returned result. Default is `250`.
 555
 556        Returns
 557        -------
 558        `aiobungie.iterators.Iterator[aiobungie.crates.Activity]`
 559            An iterator of the player's activities.
 560
 561        Raises
 562        ------
 563        `aiobungie.MembershipTypeError`
 564            The provided membership type was invalid.
 565        """
 566        resp = await self.rest.fetch_activities(
 567            member_id,
 568            character_id,
 569            mode,
 570            membership_type=membership_type,
 571            page=page,
 572            limit=limit,
 573        )
 574
 575        return self.factory.deserialize_activities(resp)
 576
 577    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
 578        """Fetch a post activity details.
 579
 580        Parameters
 581        ----------
 582        instance_id: `int`
 583            The activity instance id.
 584
 585        Returns
 586        -------
 587        `aiobungie.crates.PostActivity`
 588           A post activity object.
 589        """
 590        resp = await self.rest.fetch_post_activity(instance_id)
 591
 592        return self.factory.deserialize_post_activity(resp)
 593
 594    async def fetch_aggregated_activity_stats(
 595        self,
 596        character_id: int,
 597        membership_id: int,
 598        membership_type: typedefs.IntAnd[enums.MembershipType],
 599    ) -> iterators.Iterator[activity.AggregatedActivity]:
 600        """Fetch aggregated activity stats for a character.
 601
 602        Parameters
 603        ----------
 604        character_id: `int`
 605            The id of the character to retrieve the activities for.
 606        membership_id: `int`
 607            The id of the user that started with `4611`.
 608        membership_type: `aiobungie.internal.enums.MembershipType`
 609            The Member ship type.
 610
 611        Returns
 612        -------
 613        `aiobungie.iterators.Iterator[aiobungie.crates.AggregatedActivity]`
 614            An iterator of the player's activities.
 615
 616        Raises
 617        ------
 618        `aiobungie.MembershipTypeError`
 619            The provided membership type was invalid.
 620        """
 621        resp = await self.rest.fetch_aggregated_activity_stats(
 622            character_id, membership_id, membership_type
 623        )
 624
 625        return self.factory.deserialize_aggregated_activities(resp)
 626
 627    # * Destiny 2 Clans or GroupsV2.
 628
 629    async def fetch_clan_from_id(
 630        self,
 631        id: int,
 632        /,
 633        access_token: typing.Optional[str] = None,
 634    ) -> clans.Clan:
 635        """Fetch a Bungie Clan by its id.
 636
 637        Parameters
 638        -----------
 639        id: `int`
 640            The clan id.
 641
 642        Returns
 643        --------
 644        `aiobungie.crates.Clan`
 645            An Bungie clan.
 646
 647        Raises
 648        ------
 649        `aiobungie.NotFound`
 650            The clan was not found.
 651        """
 652        resp = await self.rest.fetch_clan_from_id(id, access_token)
 653
 654        return self.factory.deserialize_clan(resp)
 655
 656    async def fetch_clan(
 657        self,
 658        name: str,
 659        /,
 660        access_token: typing.Optional[str] = None,
 661        *,
 662        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 663    ) -> clans.Clan:
 664        """Fetch a Clan by its name.
 665        This method will return the first clan found with given name.
 666
 667        Parameters
 668        ----------
 669        name: `str`
 670            The clan name
 671
 672        Other Parameters
 673        ----------------
 674        access_token : `typing.Optional[str]`
 675            An optional access token to make the request with.
 676
 677            If the token was bound to a member of the clan,
 678            This field `aiobungie.crates.Clan.current_user_membership` will be available
 679            and will return the membership of the user who made this request.
 680        type : `aiobungie.GroupType`
 681            The group type, Default is aiobungie.GroupType.CLAN.
 682
 683        Returns
 684        -------
 685        `aiobungie.crates.Clan`
 686            A Bungie clan.
 687
 688        Raises
 689        ------
 690        `aiobungie.NotFound`
 691            The clan was not found.
 692        """
 693        resp = await self.rest.fetch_clan(name, access_token, type=type)
 694
 695        return self.factory.deserialize_clan(resp)
 696
 697    async def fetch_clan_conversations(
 698        self, clan_id: int, /
 699    ) -> collections.Sequence[clans.ClanConversation]:
 700        """Fetch the conversations/chat channels of the given clan id.
 701
 702        Parameters
 703        ----------
 704        clan_id : `int`
 705            The clan id.
 706
 707        Returns
 708        `collections.Sequence[aiobungie.crates.ClanConversation]`
 709            A sequence of the clan chat channels.
 710        """
 711        resp = await self.rest.fetch_clan_conversations(clan_id)
 712
 713        return self.factory.deserialize_clan_conversations(resp)
 714
 715    async def fetch_clan_admins(
 716        self, clan_id: int, /
 717    ) -> iterators.Iterator[clans.ClanMember]:
 718        """Fetch the clan founder and admins.
 719
 720        Parameters
 721        ----------
 722        clan_id : `int`
 723            The clan id.
 724
 725        Returns
 726        -------
 727        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
 728            An iterator over the found clan admins and founder.
 729
 730        Raises
 731        ------
 732        `aiobungie.NotFound`
 733            The requested clan was not found.
 734        """
 735        resp = await self.rest.fetch_clan_admins(clan_id)
 736
 737        return self.factory.deserialize_clan_members(resp)
 738
 739    async def fetch_groups_for_member(
 740        self,
 741        member_id: int,
 742        member_type: typedefs.IntAnd[enums.MembershipType],
 743        /,
 744        *,
 745        filter: int = 0,
 746        group_type: enums.GroupType = enums.GroupType.CLAN,
 747    ) -> collections.Sequence[clans.GroupMember]:
 748        """Fetch information about the groups that a given member has joined.
 749
 750        Parameters
 751        ----------
 752        member_id : `int`
 753            The member's id
 754        member_type : `aiobungie.MembershipType`
 755            The member's membership type.
 756
 757        Other Parameters
 758        ----------------
 759        filter : `int`
 760            Filter apply to list of joined groups. This Default to `0`
 761        group_type : `aiobungie.GroupType`
 762            The group's type.
 763            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 764
 765        Returns
 766        -------
 767        `collections.Sequence[aiobungie.crates.GroupMember]`
 768            A sequence of joined groups for the fetched member.
 769        """
 770        resp = await self.rest.fetch_groups_for_member(
 771            member_id, member_type, filter=filter, group_type=group_type
 772        )
 773
 774        return [
 775            self.factory.deserialize_group_member(group) for group in resp["results"]
 776        ]
 777
 778    async def fetch_potential_groups_for_member(
 779        self,
 780        member_id: int,
 781        member_type: typedefs.IntAnd[enums.MembershipType],
 782        /,
 783        *,
 784        filter: int = 0,
 785        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 786    ) -> collections.Sequence[clans.GroupMember]:
 787        """Fetch the potential groups for a clan member.
 788
 789        Parameters
 790        ----------
 791        member_id : `int`
 792            The member's id
 793        member_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 794            The member's membership type.
 795
 796        Other Parameters
 797        ----------------
 798        filter : `int`
 799            Filter apply to list of joined groups. This Default to `0`
 800        group_type : `aiobungie.typedefs.IntAnd[aiobungie.GroupType]`
 801            The group's type.
 802            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 803
 804        Returns
 805        -------
 806        `collections.Sequence[aiobungie.crates.GroupMember]`
 807            A sequence of joined potential groups for the fetched member.
 808        """
 809        resp = await self.rest.fetch_potential_groups_for_member(
 810            member_id, member_type, filter=filter, group_type=group_type
 811        )
 812
 813        return [
 814            self.factory.deserialize_group_member(group) for group in resp["results"]
 815        ]
 816
 817    async def fetch_clan_members(
 818        self,
 819        clan_id: int,
 820        /,
 821        *,
 822        name: typing.Optional[str] = None,
 823        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 824    ) -> iterators.Iterator[clans.ClanMember]:
 825        """Fetch Bungie clan members.
 826
 827        Parameters
 828        ----------
 829        clan_id : `int`
 830            The clans id
 831
 832        Other Parameters
 833        ----------------
 834        name : `typing.Optional[str]`
 835            If provided, Only players matching this name will be returned.
 836        type : `aiobungie.MembershipType`
 837            An optional clan member's membership type.
 838            This parameter is used to filter the returned results
 839            by the provided membership, For an example XBox memberships only,
 840            Otherwise will return all memberships.
 841
 842        Returns
 843        -------
 844        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
 845            An iterator over the bungie clan members.
 846
 847        Raises
 848        ------
 849        `aiobungie.NotFound`
 850            The clan was not found.
 851        """
 852        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
 853
 854        return self.factory.deserialize_clan_members(resp)
 855
 856    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
 857        """Fetch the clan banners.
 858
 859        Returns
 860        -------
 861        `collections.Sequence[aiobungie.crates.ClanBanner]`
 862            A sequence of the clan banners.
 863        """
 864        resp = await self.rest.fetch_clan_banners()
 865
 866        return self.factory.deserialize_clan_banners(resp)
 867
 868    # This method is required to be here since it deserialize the clan.
 869    async def kick_clan_member(
 870        self,
 871        access_token: str,
 872        /,
 873        group_id: int,
 874        membership_id: int,
 875        membership_type: typedefs.IntAnd[enums.MembershipType],
 876    ) -> clans.Clan:
 877        """Kick a member from the clan.
 878
 879        .. note::
 880            This request requires OAuth2: oauth2: `AdminGroups` scope.
 881
 882        Parameters
 883        ----------
 884        access_token : `str`
 885            The bearer access token associated with the bungie account.
 886        group_id: `int`
 887            The group id.
 888        membership_id : `int`
 889            The member id to kick.
 890        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 891            The member's membership type.
 892
 893        Returns
 894        -------
 895        `aiobungie.crates.clan.Clan`
 896            The clan that the member was kicked from.
 897        """
 898        resp = await self.rest.kick_clan_member(
 899            access_token,
 900            group_id=group_id,
 901            membership_id=membership_id,
 902            membership_type=membership_type,
 903        )
 904
 905        return self.factory.deserialize_clan(resp)
 906
 907    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
 908        """Fetch a Bungie clan's weekly reward state.
 909
 910        Parameters
 911        ----------
 912        clan_id : `int`
 913            The clan's id.
 914
 915        Returns
 916        -------
 917        `aiobungie.crates.Milestone`
 918            A runtime status of the clan's milestone data.
 919        """
 920
 921        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
 922
 923        return self.factory.deserialize_milestone(resp)
 924
 925    # * Destiny 2 Entities aka Definitions.
 926
 927    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
 928        """Fetch a static inventory item entity given a its hash.
 929
 930        Parameters
 931        ----------
 932        hash: `int`
 933            Inventory item's hash.
 934
 935        Returns
 936        -------
 937        `aiobungie.crates.InventoryEntity`
 938            A bungie inventory item.
 939        """
 940        resp = await self.rest.fetch_inventory_item(hash)
 941
 942        return self.factory.deserialize_inventory_entity(resp)
 943
 944    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
 945        """Fetch a Destiny objective entity given a its hash.
 946
 947        Parameters
 948        ----------
 949        hash: `int`
 950            objective's hash.
 951
 952        Returns
 953        -------
 954        `aiobungie.crates.ObjectiveEntity`
 955            An objective entity item.
 956        """
 957        resp = await self.rest.fetch_objective_entity(hash)
 958
 959        return self.factory.deserialize_objective_entity(resp)
 960
 961    async def search_entities(
 962        self, name: str, entity_type: str, *, page: int = 0
 963    ) -> iterators.Iterator[entity.SearchableEntity]:
 964        """Search for Destiny2 entities given a name and its type.
 965
 966        Parameters
 967        ----------
 968        name : `str`
 969            The name of the entity, i.e., Thunderlord, One thousand voices.
 970        entity_type : `str`
 971            The type of the entity, AKA Definition,
 972            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
 973
 974        Other Parameters
 975        ----------------
 976        page : `int`
 977            An optional page to return. Default to 0.
 978
 979        Returns
 980        -------
 981        `aiobungie.iterators.Iterator[aiobungie.crates.SearchableEntity]`
 982            An iterator over the found results matching the provided name.
 983        """
 984        resp = await self.rest.search_entities(name, entity_type, page=page)
 985
 986        return self.factory.deserialize_inventory_results(resp)
 987
 988    # Fireteams
 989
 990    async def fetch_fireteams(
 991        self,
 992        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
 993        *,
 994        platform: typedefs.IntAnd[
 995            fireteams.FireteamPlatform
 996        ] = fireteams.FireteamPlatform.ANY,
 997        language: typing.Union[
 998            fireteams.FireteamLanguage, str
 999        ] = fireteams.FireteamLanguage.ALL,
1000        date_range: int = 0,
1001        page: int = 0,
1002        slots_filter: int = 0,
1003    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1004        """Fetch public Bungie fireteams with open slots.
1005
1006        Parameters
1007        ----------
1008        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1009            The fireteam activity type.
1010
1011        Other Parameters
1012        ----------------
1013        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1014            If this is provided. Then the results will be filtered with the given platform.
1015            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1016        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1017            A locale language to filter the used language in that fireteam.
1018            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1019        date_range : `int`
1020            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1021        page : `int`
1022            The page number. By default its `0` which returns all available activities.
1023        slots_filter : `int`
1024            Filter the returned fireteams based on available slots. Default is `0`
1025
1026        Returns
1027        -------
1028        `typing.Optional[collections.Sequence[fireteams.Fireteam]]`
1029            A sequence of `aiobungie.crates.Fireteam` or `None`.
1030        """
1031
1032        resp = await self.rest.fetch_fireteams(
1033            activity_type,
1034            platform=platform,
1035            language=language,
1036            date_range=date_range,
1037            page=page,
1038            slots_filter=slots_filter,
1039        )
1040
1041        return self.factory.deserialize_fireteams(resp)
1042
1043    async def fetch_avaliable_clan_fireteams(
1044        self,
1045        access_token: str,
1046        group_id: int,
1047        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1048        *,
1049        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1050        language: typing.Union[fireteams.FireteamLanguage, str],
1051        date_range: int = 0,
1052        page: int = 0,
1053        public_only: bool = False,
1054        slots_filter: int = 0,
1055    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1056        """Fetch a clan's fireteams with open slots.
1057
1058        .. note::
1059            This method requires OAuth2: ReadGroups scope.
1060
1061        Parameters
1062        ----------
1063        access_token : `str`
1064            The bearer access token associated with the bungie account.
1065        group_id : `int`
1066            The group/clan id of the fireteam.
1067        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1068            The fireteam activity type.
1069
1070        Other Parameters
1071        ----------------
1072        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1073            If this is provided. Then the results will be filtered with the given platform.
1074            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1075        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1076            A locale language to filter the used language in that fireteam.
1077            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1078        date_range : `int`
1079            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1080        page : `int`
1081            The page number. By default its `0` which returns all available activities.
1082        public_only: `bool`
1083            If set to True, Then only public fireteams will be returned.
1084        slots_filter : `int`
1085            Filter the returned fireteams based on available slots. Default is `0`
1086
1087        Returns
1088        -------
1089        `typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]`
1090            A sequence of  fireteams found in the clan.
1091            `None` will be returned if nothing was found.
1092        """
1093        resp = await self.rest.fetch_avaliable_clan_fireteams(
1094            access_token,
1095            group_id,
1096            activity_type,
1097            platform=platform,
1098            language=language,
1099            date_range=date_range,
1100            page=page,
1101            public_only=public_only,
1102            slots_filter=slots_filter,
1103        )
1104
1105        return self.factory.deserialize_fireteams(resp)
1106
1107    async def fetch_clan_fireteam(
1108        self, access_token: str, fireteam_id: int, group_id: int
1109    ) -> fireteams.AvailableFireteam:
1110        """Fetch a specific clan fireteam.
1111
1112        .. note::
1113            This method requires OAuth2: ReadGroups scope.
1114
1115        Parameters
1116        ----------
1117        access_token : `str`
1118            The bearer access token associated with the bungie account.
1119        group_id : `int`
1120            The group/clan id to fetch the fireteam from.
1121        fireteam_id : `int`
1122            The fireteam id to fetch.
1123
1124        Returns
1125        -------
1126        `typing.Optional[aiobungie.crates.AvailableFireteam]`
1127            A sequence of available fireteams objects if exists. else `None` will be returned.
1128        """
1129        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1130
1131        return self.factory.deserialize_available_fireteams(
1132            resp, no_results=True
1133        )  # type: ignore[return-value]
1134
1135    async def fetch_my_clan_fireteams(
1136        self,
1137        access_token: str,
1138        group_id: int,
1139        *,
1140        include_closed: bool = True,
1141        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1142        language: typing.Union[fireteams.FireteamLanguage, str],
1143        filtered: bool = True,
1144        page: int = 0,
1145    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1146        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1147
1148        .. note::
1149            This method requires OAuth2: ReadGroups scope.
1150
1151        Parameters
1152        ----------
1153        access_token : str
1154            The bearer access token associated with the bungie account.
1155        group_id : int
1156            The group/clan id to fetch.
1157
1158        Other Parameters
1159        ----------------
1160        include_closed : bool
1161            If provided and set to True, It will also return closed fireteams.
1162            If provided and set to False, It will only return public fireteams. Default is True.
1163        platform : aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]
1164            If this is provided. Then the results will be filtered with the given platform.
1165            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1166        language : typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]
1167            A locale language to filter the used language in that fireteam.
1168            Defaults to aiobungie.crates.FireteamLanguage.ALL
1169        filtered : bool
1170            If set to True, it will filter by clan. Otherwise not. Default is True.
1171        page : int
1172            The page number. By default its 0 which returns all available activities.
1173
1174        Returns
1175        -------
1176        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1177            A sequence of available fireteams objects if exists. else `None` will be returned.
1178        """
1179        resp = await self.rest.fetch_my_clan_fireteams(
1180            access_token,
1181            group_id,
1182            include_closed=include_closed,
1183            platform=platform,
1184            language=language,
1185            filtered=filtered,
1186            page=page,
1187        )
1188
1189        return self.factory.deserialize_available_fireteams(resp)  # type: ignore[return-value]
1190
1191    # Friends and social.
1192
1193    async def fetch_friends(
1194        self, access_token: str, /
1195    ) -> collections.Sequence[friends.Friend]:
1196        """Fetch bungie friend list.
1197
1198        .. note::
1199            This requests OAuth2: ReadUserData scope.
1200
1201        Parameters
1202        -----------
1203        access_token : `str`
1204            The bearer access token associated with the bungie account.
1205
1206        Returns
1207        -------
1208        `collections.Sequence[aiobungie.crates.Friend]`
1209            A sequence of the friends associated with that access token.
1210        """
1211
1212        resp = await self.rest.fetch_friends(access_token)
1213
1214        return self.factory.deserialize_friends(resp)
1215
1216    async def fetch_friend_requests(
1217        self, access_token: str, /
1218    ) -> friends.FriendRequestView:
1219        """Fetch pending bungie friend requests queue.
1220
1221        .. note::
1222            This requests OAuth2: ReadUserData scope.
1223
1224        Parameters
1225        -----------
1226        access_token : `str`
1227            The bearer access token associated with the bungie account.
1228
1229        Returns
1230        -------
1231        `aiobungie.crates.FriendRequestView`
1232            A friend requests view of that associated access token.
1233        """
1234
1235        resp = await self.rest.fetch_friend_requests(access_token)
1236
1237        return self.factory.deserialize_friend_requests(resp)
1238
1239    # Applications and Developer portal.
1240
1241    async def fetch_application(self, appid: int, /) -> application.Application:
1242        """Fetch a Bungie application.
1243
1244        Parameters
1245        -----------
1246        appid: `int`
1247            The application id.
1248
1249        Returns
1250        --------
1251        `aiobungie.crates.Application`
1252            A Bungie application.
1253        """
1254        resp = await self.rest.fetch_application(appid)
1255
1256        return self.factory.deserialize_app(resp)
1257
1258    # Milestones
1259
1260    async def fetch_public_milestone_content(
1261        self, milestone_hash: int, /
1262    ) -> milestones.MilestoneContent:
1263        """Fetch the milestone content given its hash.
1264
1265        Parameters
1266        ----------
1267        milestone_hash : `int`
1268            The milestone hash.
1269
1270        Returns
1271        -------
1272        `aiobungie.crates.milestones.MilestoneContent`
1273            A milestone content object.
1274        """
1275        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1276
1277        return self.factory.deserialize_public_milestone_content(resp)

Standard Bungie API client application.

This client deserialize the REST JSON responses using aiobungie.Factory and returns aiobungie.crates Python object implementations of the responses.

A aiobungie.RESTClient REST client can also be used alone for low-level concepts.

Example
import aiobungie

client = aiobungie.Client('...')

async def main():
    async with client.rest:
        user = await client.fetch_current_user_memberships('...')
        print(user)
Parameters
  • token (str): Your Bungie's API key or Token from the developer's portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • max_ratelimit_retries (int): The max retries number to retry if the request hit a 429 status code. Defaults to 3.
  • client_secret (str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
Client( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, max_retries: int = 4, max_ratelimit_retries: int = 3)
102    def __init__(
103        self,
104        token: str,
105        /,
106        client_secret: typing.Optional[str] = None,
107        client_id: typing.Optional[int] = None,
108        *,
109        max_retries: int = 4,
110        max_ratelimit_retries: int = 3,
111    ) -> None:
112
113        self._rest = rest_.RESTClient(
114            token,
115            client_secret,
116            client_id,
117            max_retries=max_retries,
118            max_ratelimit_retries=max_ratelimit_retries,
119        )
120
121        self._factory = factory_.Factory(self)

Returns the marshalling factory for the client.

rest: aiobungie.interfaces.rest.RESTInterface

Returns the REST client for the this client.

request: aiobungie.Client

A readonly ClientApp instance used for external requests.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

def run( self, future: collections.abc.Coroutine[typing.Any, None, None], debug: bool = False) -> None:
139    def run(
140        self, future: collections.Coroutine[typing.Any, None, None], debug: bool = False
141    ) -> None:
142        loop: typing.Final[asyncio.AbstractEventLoop] = helpers.get_or_make_loop()
143        try:
144            if not loop.is_running():
145                loop.set_debug(debug)
146                loop.run_until_complete(future)
147
148        except Exception as exc:
149            raise RuntimeError(f"Failed to run {future.__qualname__}") from exc
150
151        except KeyboardInterrupt:
152            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")
153            return

Runs a coroutine function until its complete.

This is equivalent to asyncio.get_event_loop().run_until_complete(...)

Parameters
  • future (collections.Coroutine[None, None, None]): A coroutine object.
  • debug (bool): Either to enable asyncio debug or not. Disabled by default.
Example
async def main() -> None:
    await fetch(...)

# Run the coroutine.
client.run(main())
async def fetch_current_user_memberships(self, access_token: str, /) -> aiobungie.crates.user.User:
157    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
158        """Fetch and return a user object of the bungie net user associated with account.
159
160        .. warning::
161            This method requires OAuth2 scope and a Bearer access token.
162
163        Parameters
164        ----------
165        access_token : `str`
166            A valid Bearer access token for the authorization.
167
168        Returns
169        -------
170        `aiobungie.crates.user.User`
171            A user object includes the Destiny memberships and Bungie.net user.
172        """
173        resp = await self.rest.fetch_current_user_memberships(access_token)
174
175        return self.factory.deserialize_user(resp)

Fetch and return a user object of the bungie net user associated with account.

This method requires OAuth2 scope and a Bearer access token.

Parameters
  • access_token (str): A valid Bearer access token for the authorization.
Returns
  • aiobungie.crates.user.User: A user object includes the Destiny memberships and Bungie.net user.
async def fetch_bungie_user(self, id: int, /) -> aiobungie.crates.user.BungieUser:
177    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
178        """Fetch a Bungie user by their BungieNet id.
179
180        .. note::
181            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
182            for other memberships.
183
184        Parameters
185        ----------
186        id: `int`
187            The user id.
188
189        Returns
190        -------
191        `aiobungie.crates.user.BungieUser`
192            A Bungie user.
193
194        Raises
195        ------
196        `aiobungie.error.NotFound`
197            The user was not found.
198        """
199        payload = await self.rest.fetch_bungie_user(id)
200
201        return self.factory.deserialize_bungie_user(payload)

Fetch a Bungie user by their BungieNet id.

This returns a Bungie user membership only. Take a look at Client.fetch_membership_from_id for other memberships.

Parameters
  • id (int): The user id.
Returns
  • aiobungie.crates.user.BungieUser: A Bungie user.
Raises
async def search_users( self, name: str, /) -> aiobungie.Iterator[aiobungie.crates.user.SearchableDestinyUser]:
203    async def search_users(
204        self, name: str, /
205    ) -> iterators.Iterator[user.SearchableDestinyUser]:
206        """Search for players and return all players that matches the same name.
207
208        Parameters
209        ----------
210        name : `buildins.str`
211            The user name.
212
213        Returns
214        -------
215        `aiobungie.iterators.Iterator[aiobungie.crates.DestinyMembership]`
216            A sequence of destiny memberships.
217        """
218        payload = await self.rest.search_users(name)
219
220        return iterators.Iterator(
221            [
222                self.factory.deserialize_searched_user(user)
223                for user in payload["searchResults"]
224            ]
225        )

Search for players and return all players that matches the same name.

Parameters
  • name (buildins.str): The user name.
Returns
async def fetch_user_themes(self) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
227    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
228        """Fetch all available user themes.
229
230        Returns
231        -------
232        `collections.Sequence[aiobungie.crates.user.UserThemes]`
233            A sequence of user themes.
234        """
235        data = await self.rest.fetch_user_themes()
236
237        return self.factory.deserialize_user_themes(data)

Fetch all available user themes.

Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of user themes.
async def fetch_hard_types( self, credential: int, type: Union[int, aiobungie.CredentialType] = <CredentialType.STEAMID: 12>, /) -> aiobungie.crates.user.HardLinkedMembership:
239    async def fetch_hard_types(
240        self,
241        credential: int,
242        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
243        /,
244    ) -> user.HardLinkedMembership:
245        """Gets any hard linked membership given a credential.
246        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
247        Cross Save aware.
248
249        Parameters
250        ----------
251        credential: `int`
252            A valid SteamID64
253        type: `aiobungie.CredentialType`
254            The credential type. This must not be changed
255            Since its only credential that works "currently"
256
257        Returns
258        -------
259        `aiobungie.crates.user.HardLinkedMembership`
260            Information about the hard linked data.
261        """
262
263        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
264
265        return user.HardLinkedMembership(
266            id=int(payload["membershipId"]),
267            type=enums.MembershipType(payload["membershipType"]),
268            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
269        )

Gets any hard linked membership given a credential. Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
  • credential (int): A valid SteamID64
  • type (aiobungie.CredentialType): The credential type. This must not be changed Since its only credential that works "currently"
Returns
  • aiobungie.crates.user.HardLinkedMembership: Information about the hard linked data.
async def fetch_membership_from_id( self, id: int, /, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> aiobungie.crates.user.User:
271    async def fetch_membership_from_id(
272        self,
273        id: int,
274        /,
275        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
276    ) -> user.User:
277        """Fetch Bungie user's memberships from their id.
278
279        Notes
280        -----
281        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
282        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
283        see `aiobungie.crates.user.DestinyMembership` for more details.
284        * If you only want the bungie user. Consider using `Client.fetch_user` method.
285
286        Parameters
287        ----------
288        id : `int`
289            The user's id.
290        type : `aiobungie.MembershipType`
291            The user's membership type.
292
293        Returns
294        -------
295        `aiobungie.crates.User`
296            A Bungie user with their membership types.
297
298        Raises
299        ------
300        aiobungie.NotFound
301            The requested user was not found.
302        """
303        payload = await self.rest.fetch_membership_from_id(id, type)
304
305        return self.factory.deserialize_user(payload)

Fetch Bungie user's memberships from their id.

Notes
  • This returns both BungieNet membership and a sequence of the player's DestinyMemberships Which includes Stadia, Xbox, Steam and PSN memberships if the player has them, see aiobungie.crates.user.DestinyMembership for more details.
  • If you only want the bungie user. Consider using Client.fetch_user method.
Parameters
Returns
Raises
async def fetch_user_credentials( self, access_token: str, membership_id: int, /) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
307    async def fetch_user_credentials(
308        self, access_token: str, membership_id: int, /
309    ) -> collections.Sequence[user.UserCredentials]:
310        """Fetch an array of credential types attached to the requested account.
311
312        .. note::
313            This method require OAuth2 Bearer access token.
314
315        Parameters
316        ----------
317        access_token : `str`
318            The bearer access token associated with the bungie account.
319        membership_id : `int`
320            The id of the membership to return.
321
322        Returns
323        -------
324        `collections.Sequence[aiobungie.crates.UserCredentials]`
325            A sequence of the attached user credentials.
326
327        Raises
328        ------
329        `aiobungie.Unauthorized`
330            The access token was wrong or no access token passed.
331        """
332        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
333
334        return self.factory.deserialize_user_credentials(resp)

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def fetch_profile( self, member_id: int, type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> aiobungie.crates.components.Component:
338    async def fetch_profile(
339        self,
340        member_id: int,
341        type: typedefs.IntAnd[enums.MembershipType],
342        components: list[enums.ComponentType],
343        auth: typing.Optional[str] = None,
344    ) -> components.Component:
345        """
346        Fetch a bungie profile passing components to the request.
347
348        Parameters
349        ----------
350        member_id: `int`
351            The member's id.
352        type: `aiobungie.MembershipType`
353            A valid membership type.
354        components : `list[aiobungie.ComponentType]`
355            List of profile components to collect and return.
356
357        Other Parameters
358        ----------------
359        auth : `typing.Optional[str]`
360            A Bearer access_token to make the request with.
361            This is optional and limited to components that only requires an Authorization token.
362
363        Returns
364        --------
365        `aiobungie.crates.Component`
366            A Destiny 2 player profile with its components.
367            Only passed components will be available if they exists. Otherwise they will be `None`
368
369        Raises
370        ------
371        `aiobungie.MembershipTypeError`
372            The provided membership type was invalid.
373        """
374        data = await self.rest.fetch_profile(member_id, type, components, auth)
375        return self.factory.deserialize_components(data)

Fetch a bungie profile passing components to the request.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
  • aiobungie.crates.Component: A Destiny 2 player profile with its components. Only passed components will be available if they exists. Otherwise they will be None
Raises
async def fetch_linked_profiles( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, all: bool = False) -> aiobungie.crates.profile.LinkedProfile:
377    async def fetch_linked_profiles(
378        self,
379        member_id: int,
380        member_type: typedefs.IntAnd[enums.MembershipType],
381        /,
382        *,
383        all: bool = False,
384    ) -> profile.LinkedProfile:
385        """Returns a summary information about all profiles linked to the requested member.
386
387        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
388
389        .. note::
390            It will only return linked accounts whose linkages you are allowed to view.
391
392        Parameters
393        ----------
394        member_id : `int`
395            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
396        member_type : `aiobungie.MembershipType`
397            The type for the membership whose linked Destiny account you want to return.
398
399        Other Parameters
400        ----------------
401        all : `bool`
402            If provided and set to `True`, All memberships regardless
403            of whether they're obscured by overrides will be returned,
404
405            If provided and set to `False`, Only available memberships will be returned.
406            The default for this is `False`.
407
408        Returns
409        -------
410        `aiobungie.crates.profile.LinkedProfile`
411            A linked profile object.
412        """
413        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
414
415        return self.factory.deserialize_linked_profiles(resp)

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
  • member_id (int): The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
  • member_type (aiobungie.MembershipType): The type for the membership whose linked Destiny account you want to return.
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether they're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
  • aiobungie.crates.profile.LinkedProfile: A linked profile object.
async def fetch_player( self, name: str, code: int, /, type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
417    async def fetch_player(
418        self,
419        name: str,
420        code: int,
421        /,
422        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
423    ) -> collections.Sequence[user.DestinyMembership]:
424        """Fetch a Destiny 2 player's memberships.
425
426        Parameters
427        -----------
428        name: `str`
429            The unique Bungie player name.
430        code : `int`
431            The unique Bungie display name code.
432        type: `aiobungie.internal.enums.MembershipType`
433            The player's membership type, e,g. XBOX, STEAM, PSN
434
435        Returns
436        --------
437        `collections.Sequence[aiobungie.crates.DestinyMembership]`
438            A sequence of the found Destiny 2 player memberships.
439            An empty sequence will be returned if no one found.
440
441        Raises
442        ------
443        `aiobungie.MembershipTypeError`
444            The provided membership type was invalid.
445        """
446        resp = await self.rest.fetch_player(name, code, type)
447
448        return self.factory.deserialize_destiny_memberships(resp)

Fetch a Destiny 2 player's memberships.

Parameters
  • name (str): The unique Bungie player name.
  • code (int): The unique Bungie display name code.
  • type (aiobungie.MembershipType): The player's membership type, e,g. XBOX, STEAM, PSN
Returns
Raises
async def fetch_character( self, member_id: int, membership_type: Union[int, aiobungie.MembershipType], character_id: int, components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> aiobungie.crates.components.CharacterComponent:
450    async def fetch_character(
451        self,
452        member_id: int,
453        membership_type: typedefs.IntAnd[enums.MembershipType],
454        character_id: int,
455        components: list[enums.ComponentType],
456        auth: typing.Optional[str] = None,
457    ) -> components.CharacterComponent:
458        """Fetch a Destiny 2 character.
459
460        Parameters
461        ----------
462        member_id: `int`
463            A valid bungie member id.
464        character_id: `int`
465            The Destiny character id to retrieve.
466        membership_type: `aiobungie.internal.enums.MembershipType`
467            The member's membership type.
468        components: `list[aiobungie.ComponentType]`
469            Multiple arguments of character components to collect and return.
470
471        Other Parameters
472        ----------------
473        auth : `typing.Optional[str]`
474            A Bearer access_token to make the request with.
475            This is optional and limited to components that only requires an Authorization token.
476
477        Returns
478        -------
479        `aiobungie.crates.CharacterComponent`
480            A Bungie character component.
481
482        `aiobungie.MembershipTypeError`
483            The provided membership type was invalid.
484        """
485        resp = await self.rest.fetch_character(
486            member_id, membership_type, character_id, components, auth
487        )
488
489        return self.factory.deserialize_character_component(resp)

Fetch a Destiny 2 character.

Parameters
  • member_id (int): A valid bungie member id.
  • character_id (int): The Destiny character id to retrieve.
  • membership_type (aiobungie.MembershipType): The member's membership type.
  • components (list[aiobungie.ComponentType]): Multiple arguments of character components to collect and return.
Other Parameters
  • auth (typing.Optional[str]): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> collections.abc.Sequence[aiobungie.crates.activity.ExtendedWeaponValues]:
491    async def fetch_unique_weapon_history(
492        self,
493        membership_id: int,
494        character_id: int,
495        membership_type: typedefs.IntAnd[enums.MembershipType],
496    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
497        """Fetch details about unique weapon usage for a character. Includes all exotics.
498
499        Parameters
500        ----------
501        membership_id : `int`
502            The Destiny user membership id.
503        character_id : `int`
504            The character id to retrieve.
505        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
506            The Destiny user's membership type.
507
508        Returns
509        -------
510        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
511            A sequence of the weapon's extended values.
512        """
513        resp = await self._rest.fetch_unique_weapon_history(
514            membership_id, character_id, membership_type
515        )
516
517        return [
518            self._factory.deserialize_extended_weapon_values(weapon)
519            for weapon in resp["weapons"]
520        ]

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
Returns
async def fetch_activities( self, member_id: int, character_id: int, mode: Union[int, aiobungie.GameMode], *, membership_type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, page: int = 0, limit: int = 250) -> aiobungie.Iterator[aiobungie.crates.activity.Activity]:
524    async def fetch_activities(
525        self,
526        member_id: int,
527        character_id: int,
528        mode: typedefs.IntAnd[enums.GameMode],
529        *,
530        membership_type: typedefs.IntAnd[
531            enums.MembershipType
532        ] = enums.MembershipType.ALL,
533        page: int = 0,
534        limit: int = 250,
535    ) -> iterators.Iterator[activity.Activity]:
536        """Fetch a Destiny 2 activity for the specified character id.
537
538        Parameters
539        ----------
540        member_id: `int`
541            The user id that starts with `4611`.
542        character_id: `int`
543            The id of the character to retrieve the activities for.
544        mode: `aiobungie.typedefs.IntAnd[aiobungie.internal.enums.GameMode]`
545            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
546
547        Other Parameters
548        ----------------
549        membership_type: `aiobungie.internal.enums.MembershipType`
550            The Member ship type, if nothing was passed than it will return all.
551        page: int
552            The page number. Default is `0`
553        limit: int
554            Limit the returned result. Default is `250`.
555
556        Returns
557        -------
558        `aiobungie.iterators.Iterator[aiobungie.crates.Activity]`
559            An iterator of the player's activities.
560
561        Raises
562        ------
563        `aiobungie.MembershipTypeError`
564            The provided membership type was invalid.
565        """
566        resp = await self.rest.fetch_activities(
567            member_id,
568            character_id,
569            mode,
570            membership_type=membership_type,
571            page=page,
572            limit=limit,
573        )
574
575        return self.factory.deserialize_activities(resp)

Fetch a Destiny 2 activity for the specified character id.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve the activities for.
  • mode (aiobungie.typedefs.IntAnd[aiobungie.GameMode]): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
  • membership_type (aiobungie.MembershipType): The Member ship type, if nothing was passed than it will return all.
  • page (int): The page number. Default is 0
  • limit (int): Limit the returned result. Default is 250.
Returns
Raises
async def fetch_post_activity(self, instance_id: int, /) -> aiobungie.crates.activity.PostActivity:
577    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
578        """Fetch a post activity details.
579
580        Parameters
581        ----------
582        instance_id: `int`
583            The activity instance id.
584
585        Returns
586        -------
587        `aiobungie.crates.PostActivity`
588           A post activity object.
589        """
590        resp = await self.rest.fetch_post_activity(instance_id)
591
592        return self.factory.deserialize_post_activity(resp)

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> aiobungie.Iterator[aiobungie.crates.activity.AggregatedActivity]:
594    async def fetch_aggregated_activity_stats(
595        self,
596        character_id: int,
597        membership_id: int,
598        membership_type: typedefs.IntAnd[enums.MembershipType],
599    ) -> iterators.Iterator[activity.AggregatedActivity]:
600        """Fetch aggregated activity stats for a character.
601
602        Parameters
603        ----------
604        character_id: `int`
605            The id of the character to retrieve the activities for.
606        membership_id: `int`
607            The id of the user that started with `4611`.
608        membership_type: `aiobungie.internal.enums.MembershipType`
609            The Member ship type.
610
611        Returns
612        -------
613        `aiobungie.iterators.Iterator[aiobungie.crates.AggregatedActivity]`
614            An iterator of the player's activities.
615
616        Raises
617        ------
618        `aiobungie.MembershipTypeError`
619            The provided membership type was invalid.
620        """
621        resp = await self.rest.fetch_aggregated_activity_stats(
622            character_id, membership_id, membership_type
623        )
624
625        return self.factory.deserialize_aggregated_activities(resp)

Fetch aggregated activity stats for a character.

Parameters
  • character_id (int): The id of the character to retrieve the activities for.
  • membership_id (int): The id of the user that started with 4611.
  • membership_type (aiobungie.MembershipType): The Member ship type.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: Optional[str] = None) -> aiobungie.crates.clans.Clan:
629    async def fetch_clan_from_id(
630        self,
631        id: int,
632        /,
633        access_token: typing.Optional[str] = None,
634    ) -> clans.Clan:
635        """Fetch a Bungie Clan by its id.
636
637        Parameters
638        -----------
639        id: `int`
640            The clan id.
641
642        Returns
643        --------
644        `aiobungie.crates.Clan`
645            An Bungie clan.
646
647        Raises
648        ------
649        `aiobungie.NotFound`
650            The clan was not found.
651        """
652        resp = await self.rest.fetch_clan_from_id(id, access_token)
653
654        return self.factory.deserialize_clan(resp)

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Returns
Raises
async def fetch_clan( self, name: str, /, access_token: Optional[str] = None, *, type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> aiobungie.crates.clans.Clan:
656    async def fetch_clan(
657        self,
658        name: str,
659        /,
660        access_token: typing.Optional[str] = None,
661        *,
662        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
663    ) -> clans.Clan:
664        """Fetch a Clan by its name.
665        This method will return the first clan found with given name.
666
667        Parameters
668        ----------
669        name: `str`
670            The clan name
671
672        Other Parameters
673        ----------------
674        access_token : `typing.Optional[str]`
675            An optional access token to make the request with.
676
677            If the token was bound to a member of the clan,
678            This field `aiobungie.crates.Clan.current_user_membership` will be available
679            and will return the membership of the user who made this request.
680        type : `aiobungie.GroupType`
681            The group type, Default is aiobungie.GroupType.CLAN.
682
683        Returns
684        -------
685        `aiobungie.crates.Clan`
686            A Bungie clan.
687
688        Raises
689        ------
690        `aiobungie.NotFound`
691            The clan was not found.
692        """
693        resp = await self.rest.fetch_clan(name, access_token, type=type)
694
695        return self.factory.deserialize_clan(resp)

Fetch a Clan by its name. This method will return the first clan found with given name.

Parameters
  • name (str): The clan name
Other Parameters
Returns
Raises
async def fetch_clan_conversations( self, clan_id: int, /) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
697    async def fetch_clan_conversations(
698        self, clan_id: int, /
699    ) -> collections.Sequence[clans.ClanConversation]:
700        """Fetch the conversations/chat channels of the given clan id.
701
702        Parameters
703        ----------
704        clan_id : `int`
705            The clan id.
706
707        Returns
708        `collections.Sequence[aiobungie.crates.ClanConversation]`
709            A sequence of the clan chat channels.
710        """
711        resp = await self.rest.fetch_clan_conversations(clan_id)
712
713        return self.factory.deserialize_clan_conversations(resp)

Fetch the conversations/chat channels of the given clan id.

Parameters
async def fetch_clan_admins( self, clan_id: int, /) -> aiobungie.Iterator[aiobungie.crates.clans.ClanMember]:
715    async def fetch_clan_admins(
716        self, clan_id: int, /
717    ) -> iterators.Iterator[clans.ClanMember]:
718        """Fetch the clan founder and admins.
719
720        Parameters
721        ----------
722        clan_id : `int`
723            The clan id.
724
725        Returns
726        -------
727        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
728            An iterator over the found clan admins and founder.
729
730        Raises
731        ------
732        `aiobungie.NotFound`
733            The requested clan was not found.
734        """
735        resp = await self.rest.fetch_clan_admins(clan_id)
736
737        return self.factory.deserialize_clan_members(resp)

Fetch the clan founder and admins.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: aiobungie.GroupType = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
739    async def fetch_groups_for_member(
740        self,
741        member_id: int,
742        member_type: typedefs.IntAnd[enums.MembershipType],
743        /,
744        *,
745        filter: int = 0,
746        group_type: enums.GroupType = enums.GroupType.CLAN,
747    ) -> collections.Sequence[clans.GroupMember]:
748        """Fetch information about the groups that a given member has joined.
749
750        Parameters
751        ----------
752        member_id : `int`
753            The member's id
754        member_type : `aiobungie.MembershipType`
755            The member's membership type.
756
757        Other Parameters
758        ----------------
759        filter : `int`
760            Filter apply to list of joined groups. This Default to `0`
761        group_type : `aiobungie.GroupType`
762            The group's type.
763            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
764
765        Returns
766        -------
767        `collections.Sequence[aiobungie.crates.GroupMember]`
768            A sequence of joined groups for the fetched member.
769        """
770        resp = await self.rest.fetch_groups_for_member(
771            member_id, member_type, filter=filter, group_type=group_type
772        )
773
774        return [
775            self.factory.deserialize_group_member(group) for group in resp["results"]
776        ]

Fetch information about the groups that a given member has joined.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
778    async def fetch_potential_groups_for_member(
779        self,
780        member_id: int,
781        member_type: typedefs.IntAnd[enums.MembershipType],
782        /,
783        *,
784        filter: int = 0,
785        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
786    ) -> collections.Sequence[clans.GroupMember]:
787        """Fetch the potential groups for a clan member.
788
789        Parameters
790        ----------
791        member_id : `int`
792            The member's id
793        member_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
794            The member's membership type.
795
796        Other Parameters
797        ----------------
798        filter : `int`
799            Filter apply to list of joined groups. This Default to `0`
800        group_type : `aiobungie.typedefs.IntAnd[aiobungie.GroupType]`
801            The group's type.
802            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
803
804        Returns
805        -------
806        `collections.Sequence[aiobungie.crates.GroupMember]`
807            A sequence of joined potential groups for the fetched member.
808        """
809        resp = await self.rest.fetch_potential_groups_for_member(
810            member_id, member_type, filter=filter, group_type=group_type
811        )
812
813        return [
814            self.factory.deserialize_group_member(group) for group in resp["results"]
815        ]

Fetch the potential groups for a clan member.

Parameters
Other Parameters
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: Optional[str] = None, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> aiobungie.Iterator[aiobungie.crates.clans.ClanMember]:
817    async def fetch_clan_members(
818        self,
819        clan_id: int,
820        /,
821        *,
822        name: typing.Optional[str] = None,
823        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
824    ) -> iterators.Iterator[clans.ClanMember]:
825        """Fetch Bungie clan members.
826
827        Parameters
828        ----------
829        clan_id : `int`
830            The clans id
831
832        Other Parameters
833        ----------------
834        name : `typing.Optional[str]`
835            If provided, Only players matching this name will be returned.
836        type : `aiobungie.MembershipType`
837            An optional clan member's membership type.
838            This parameter is used to filter the returned results
839            by the provided membership, For an example XBox memberships only,
840            Otherwise will return all memberships.
841
842        Returns
843        -------
844        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
845            An iterator over the bungie clan members.
846
847        Raises
848        ------
849        `aiobungie.NotFound`
850            The clan was not found.
851        """
852        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
853
854        return self.factory.deserialize_clan_members(resp)

Fetch Bungie clan members.

Parameters
  • clan_id (int): The clans id
Other Parameters
  • name (typing.Optional[str]): If provided, Only players matching this name will be returned.
  • type (aiobungie.MembershipType): An optional clan member's membership type. This parameter is used to filter the returned results by the provided membership, For an example XBox memberships only, Otherwise will return all memberships.
Returns
Raises
async def fetch_clan_banners(self) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
856    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
857        """Fetch the clan banners.
858
859        Returns
860        -------
861        `collections.Sequence[aiobungie.crates.ClanBanner]`
862            A sequence of the clan banners.
863        """
864        resp = await self.rest.fetch_clan_banners()
865
866        return self.factory.deserialize_clan_banners(resp)

Fetch the clan banners.

Returns
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> aiobungie.crates.clans.Clan:
869    async def kick_clan_member(
870        self,
871        access_token: str,
872        /,
873        group_id: int,
874        membership_id: int,
875        membership_type: typedefs.IntAnd[enums.MembershipType],
876    ) -> clans.Clan:
877        """Kick a member from the clan.
878
879        .. note::
880            This request requires OAuth2: oauth2: `AdminGroups` scope.
881
882        Parameters
883        ----------
884        access_token : `str`
885            The bearer access token associated with the bungie account.
886        group_id: `int`
887            The group id.
888        membership_id : `int`
889            The member id to kick.
890        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
891            The member's membership type.
892
893        Returns
894        -------
895        `aiobungie.crates.clan.Clan`
896            The clan that the member was kicked from.
897        """
898        resp = await self.rest.kick_clan_member(
899            access_token,
900            group_id=group_id,
901            membership_id=membership_id,
902            membership_type=membership_type,
903        )
904
905        return self.factory.deserialize_clan(resp)

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Returns
  • aiobungie.crates.clan.Clan: The clan that the member was kicked from.
async def fetch_clan_weekly_rewards(self, clan_id: int) -> aiobungie.crates.milestones.Milestone:
907    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
908        """Fetch a Bungie clan's weekly reward state.
909
910        Parameters
911        ----------
912        clan_id : `int`
913            The clan's id.
914
915        Returns
916        -------
917        `aiobungie.crates.Milestone`
918            A runtime status of the clan's milestone data.
919        """
920
921        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
922
923        return self.factory.deserialize_milestone(resp)

Fetch a Bungie clan's weekly reward state.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_inventory_item(self, hash: int, /) -> aiobungie.crates.entity.InventoryEntity:
927    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
928        """Fetch a static inventory item entity given a its hash.
929
930        Parameters
931        ----------
932        hash: `int`
933            Inventory item's hash.
934
935        Returns
936        -------
937        `aiobungie.crates.InventoryEntity`
938            A bungie inventory item.
939        """
940        resp = await self.rest.fetch_inventory_item(hash)
941
942        return self.factory.deserialize_inventory_entity(resp)

Fetch a static inventory item entity given a its hash.

Parameters
  • hash (int): Inventory item's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> aiobungie.crates.entity.ObjectiveEntity:
944    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
945        """Fetch a Destiny objective entity given a its hash.
946
947        Parameters
948        ----------
949        hash: `int`
950            objective's hash.
951
952        Returns
953        -------
954        `aiobungie.crates.ObjectiveEntity`
955            An objective entity item.
956        """
957        resp = await self.rest.fetch_objective_entity(hash)
958
959        return self.factory.deserialize_objective_entity(resp)

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> aiobungie.Iterator[aiobungie.crates.entity.SearchableEntity]:
961    async def search_entities(
962        self, name: str, entity_type: str, *, page: int = 0
963    ) -> iterators.Iterator[entity.SearchableEntity]:
964        """Search for Destiny2 entities given a name and its type.
965
966        Parameters
967        ----------
968        name : `str`
969            The name of the entity, i.e., Thunderlord, One thousand voices.
970        entity_type : `str`
971            The type of the entity, AKA Definition,
972            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
973
974        Other Parameters
975        ----------------
976        page : `int`
977            An optional page to return. Default to 0.
978
979        Returns
980        -------
981        `aiobungie.iterators.Iterator[aiobungie.crates.SearchableEntity]`
982            An iterator over the found results matching the provided name.
983        """
984        resp = await self.rest.search_entities(name, entity_type, page=page)
985
986        return self.factory.deserialize_inventory_results(resp)

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition for emblems, weapons, and other inventory items.
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_fireteams( self, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform] = <FireteamPlatform.ANY: 0>, language: Union[aiobungie.FireteamLanguage, str] = <FireteamLanguage.ALL: >, date_range: int = 0, page: int = 0, slots_filter: int = 0) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
 990    async def fetch_fireteams(
 991        self,
 992        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
 993        *,
 994        platform: typedefs.IntAnd[
 995            fireteams.FireteamPlatform
 996        ] = fireteams.FireteamPlatform.ANY,
 997        language: typing.Union[
 998            fireteams.FireteamLanguage, str
 999        ] = fireteams.FireteamLanguage.ALL,
1000        date_range: int = 0,
1001        page: int = 0,
1002        slots_filter: int = 0,
1003    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1004        """Fetch public Bungie fireteams with open slots.
1005
1006        Parameters
1007        ----------
1008        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1009            The fireteam activity type.
1010
1011        Other Parameters
1012        ----------------
1013        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1014            If this is provided. Then the results will be filtered with the given platform.
1015            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1016        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1017            A locale language to filter the used language in that fireteam.
1018            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1019        date_range : `int`
1020            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1021        page : `int`
1022            The page number. By default its `0` which returns all available activities.
1023        slots_filter : `int`
1024            Filter the returned fireteams based on available slots. Default is `0`
1025
1026        Returns
1027        -------
1028        `typing.Optional[collections.Sequence[fireteams.Fireteam]]`
1029            A sequence of `aiobungie.crates.Fireteam` or `None`.
1030        """
1031
1032        resp = await self.rest.fetch_fireteams(
1033            activity_type,
1034            platform=platform,
1035            language=language,
1036            date_range=date_range,
1037            page=page,
1038            slots_filter=slots_filter,
1039        )
1040
1041        return self.factory.deserialize_fireteams(resp)

Fetch public Bungie fireteams with open slots.

Parameters
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_avaliable_clan_fireteams( self, access_token: str, group_id: int, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], date_range: int = 0, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
1043    async def fetch_avaliable_clan_fireteams(
1044        self,
1045        access_token: str,
1046        group_id: int,
1047        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1048        *,
1049        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1050        language: typing.Union[fireteams.FireteamLanguage, str],
1051        date_range: int = 0,
1052        page: int = 0,
1053        public_only: bool = False,
1054        slots_filter: int = 0,
1055    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1056        """Fetch a clan's fireteams with open slots.
1057
1058        .. note::
1059            This method requires OAuth2: ReadGroups scope.
1060
1061        Parameters
1062        ----------
1063        access_token : `str`
1064            The bearer access token associated with the bungie account.
1065        group_id : `int`
1066            The group/clan id of the fireteam.
1067        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1068            The fireteam activity type.
1069
1070        Other Parameters
1071        ----------------
1072        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1073            If this is provided. Then the results will be filtered with the given platform.
1074            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1075        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1076            A locale language to filter the used language in that fireteam.
1077            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1078        date_range : `int`
1079            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1080        page : `int`
1081            The page number. By default its `0` which returns all available activities.
1082        public_only: `bool`
1083            If set to True, Then only public fireteams will be returned.
1084        slots_filter : `int`
1085            Filter the returned fireteams based on available slots. Default is `0`
1086
1087        Returns
1088        -------
1089        `typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]`
1090            A sequence of  fireteams found in the clan.
1091            `None` will be returned if nothing was found.
1092        """
1093        resp = await self.rest.fetch_avaliable_clan_fireteams(
1094            access_token,
1095            group_id,
1096            activity_type,
1097            platform=platform,
1098            language=language,
1099            date_range=date_range,
1100            page=page,
1101            public_only=public_only,
1102            slots_filter=slots_filter,
1103        )
1104
1105        return self.factory.deserialize_fireteams(resp)

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]): The fireteam activity type.
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to 0.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
  • typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]: A sequence of fireteams found in the clan. None will be returned if nothing was found.
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> aiobungie.crates.fireteams.AvailableFireteam:
1107    async def fetch_clan_fireteam(
1108        self, access_token: str, fireteam_id: int, group_id: int
1109    ) -> fireteams.AvailableFireteam:
1110        """Fetch a specific clan fireteam.
1111
1112        .. note::
1113            This method requires OAuth2: ReadGroups scope.
1114
1115        Parameters
1116        ----------
1117        access_token : `str`
1118            The bearer access token associated with the bungie account.
1119        group_id : `int`
1120            The group/clan id to fetch the fireteam from.
1121        fireteam_id : `int`
1122            The fireteam id to fetch.
1123
1124        Returns
1125        -------
1126        `typing.Optional[aiobungie.crates.AvailableFireteam]`
1127            A sequence of available fireteams objects if exists. else `None` will be returned.
1128        """
1129        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1130
1131        return self.factory.deserialize_available_fireteams(
1132            resp, no_results=True
1133        )  # type: ignore[return-value]

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], filtered: bool = True, page: int = 0) -> collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]:
1135    async def fetch_my_clan_fireteams(
1136        self,
1137        access_token: str,
1138        group_id: int,
1139        *,
1140        include_closed: bool = True,
1141        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1142        language: typing.Union[fireteams.FireteamLanguage, str],
1143        filtered: bool = True,
1144        page: int = 0,
1145    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1146        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1147
1148        .. note::
1149            This method requires OAuth2: ReadGroups scope.
1150
1151        Parameters
1152        ----------
1153        access_token : str
1154            The bearer access token associated with the bungie account.
1155        group_id : int
1156            The group/clan id to fetch.
1157
1158        Other Parameters
1159        ----------------
1160        include_closed : bool
1161            If provided and set to True, It will also return closed fireteams.
1162            If provided and set to False, It will only return public fireteams. Default is True.
1163        platform : aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]
1164            If this is provided. Then the results will be filtered with the given platform.
1165            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1166        language : typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]
1167            A locale language to filter the used language in that fireteam.
1168            Defaults to aiobungie.crates.FireteamLanguage.ALL
1169        filtered : bool
1170            If set to True, it will filter by clan. Otherwise not. Default is True.
1171        page : int
1172            The page number. By default its 0 which returns all available activities.
1173
1174        Returns
1175        -------
1176        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1177            A sequence of available fireteams objects if exists. else `None` will be returned.
1178        """
1179        resp = await self.rest.fetch_my_clan_fireteams(
1180            access_token,
1181            group_id,
1182            include_closed=include_closed,
1183            platform=platform,
1184            language=language,
1185            filtered=filtered,
1186            page=page,
1187        )
1188
1189        return self.factory.deserialize_available_fireteams(resp)  # type: ignore[return-value]

A method that's similar to fetch_fireteams but requires OAuth2.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_friends( self, access_token: str, /) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
1193    async def fetch_friends(
1194        self, access_token: str, /
1195    ) -> collections.Sequence[friends.Friend]:
1196        """Fetch bungie friend list.
1197
1198        .. note::
1199            This requests OAuth2: ReadUserData scope.
1200
1201        Parameters
1202        -----------
1203        access_token : `str`
1204            The bearer access token associated with the bungie account.
1205
1206        Returns
1207        -------
1208        `collections.Sequence[aiobungie.crates.Friend]`
1209            A sequence of the friends associated with that access token.
1210        """
1211
1212        resp = await self.rest.fetch_friends(access_token)
1213
1214        return self.factory.deserialize_friends(resp)

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> aiobungie.crates.friends.FriendRequestView:
1216    async def fetch_friend_requests(
1217        self, access_token: str, /
1218    ) -> friends.FriendRequestView:
1219        """Fetch pending bungie friend requests queue.
1220
1221        .. note::
1222            This requests OAuth2: ReadUserData scope.
1223
1224        Parameters
1225        -----------
1226        access_token : `str`
1227            The bearer access token associated with the bungie account.
1228
1229        Returns
1230        -------
1231        `aiobungie.crates.FriendRequestView`
1232            A friend requests view of that associated access token.
1233        """
1234
1235        resp = await self.rest.fetch_friend_requests(access_token)
1236
1237        return self.factory.deserialize_friend_requests(resp)

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_application(self, appid: int, /) -> aiobungie.crates.application.Application:
1241    async def fetch_application(self, appid: int, /) -> application.Application:
1242        """Fetch a Bungie application.
1243
1244        Parameters
1245        -----------
1246        appid: `int`
1247            The application id.
1248
1249        Returns
1250        --------
1251        `aiobungie.crates.Application`
1252            A Bungie application.
1253        """
1254        resp = await self.rest.fetch_application(appid)
1255
1256        return self.factory.deserialize_app(resp)

Fetch a Bungie application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_public_milestone_content( self, milestone_hash: int, /) -> aiobungie.crates.milestones.MilestoneContent:
1260    async def fetch_public_milestone_content(
1261        self, milestone_hash: int, /
1262    ) -> milestones.MilestoneContent:
1263        """Fetch the milestone content given its hash.
1264
1265        Parameters
1266        ----------
1267        milestone_hash : `int`
1268            The milestone hash.
1269
1270        Returns
1271        -------
1272        `aiobungie.crates.milestones.MilestoneContent`
1273            A milestone content object.
1274        """
1275        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1276
1277        return self.factory.deserialize_public_milestone_content(resp)

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
  • aiobungie.crates.milestones.MilestoneContent: A milestone content object.
@typing.final
class ClosedReasons(aiobungie.Flag):
779@typing.final
780class ClosedReasons(Flag):
781    """A Flags enumeration representing the reasons why a person can't join this user's fireteam."""
782
783    NONE = 0
784    MATCHMAKING = 1 << 0
785    LOADING = 1 << 1
786    SOLO = 1 << 2
787    """The activity is required to be played solo."""
788    INTERNAL_REASONS = 1 << 3
789    """
790    The user can't be joined for one of a variety of internal reasons.
791    Basically, the game can't let you join at this time,
792    but for reasons that aren't under the control of this user
793    """
794    DISALLOWED_BY_GAME_STATE = 1 << 4
795    """The user's current activity/quest/other transitory game state is preventing joining."""
796    OFFLINE = 32768
797    """The user appears offline."""

A Flags enumeration representing the reasons why a person can't join this user's fireteam.

NONE = <ClosedReasons.NONE: 0>
MATCHMAKING = <ClosedReasons.MATCHMAKING: 1>
LOADING = <ClosedReasons.LOADING: 2>
SOLO = <ClosedReasons.SOLO: 4>

The activity is required to be played solo.

INTERNAL_REASONS = <ClosedReasons.INTERNAL_REASONS: 8>

The user can't be joined for one of a variety of internal reasons. Basically, the game can't let you join at this time, but for reasons that aren't under the control of this user

DISALLOWED_BY_GAME_STATE = <ClosedReasons.DISALLOWED_BY_GAME_STATE: 16>

The user's current activity/quest/other transitory game state is preventing joining.

OFFLINE = <ClosedReasons.OFFLINE: 32768>

The user appears offline.

Inherited Members
Flag
name
value
@typing.final
class ComponentFields(aiobungie.Enum):
74@typing.final
75class ComponentFields(enums.Enum):
76    """An enum that provides fields found in a base component response."""
77
78    PRIVACY = ComponentPrivacy
79    DISABLED = False

An enum that provides fields found in a base component response.

PRIVACY = <ComponentFields.PRIVACY: <enum 'ComponentPrivacy'>>
DISABLED = <ComponentFields.DISABLED: False>
Inherited Members
Enum
name
value
@typing.final
class ComponentPrivacy(builtins.int, aiobungie.Enum):
65@typing.final
66class ComponentPrivacy(int, enums.Enum):
67    """An enum the provides privacy settings for profile components."""
68
69    NONE = 0
70    PUBLIC = 1
71    PRIVATE = 2

An enum the provides privacy settings for profile components.

NONE = <ComponentPrivacy.NONE: 0>
PUBLIC = <ComponentPrivacy.PUBLIC: 1>
PRIVATE = <ComponentPrivacy.PRIVATE: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ComponentType(aiobungie.Enum):
358@typing.final
359class ComponentType(Enum):
360    """An Enum for Destiny 2 profile Components."""
361
362    NONE = 0
363
364    PROFILE = 100
365    PROFILE_INVENTORIES = 102
366    PROFILE_CURRENCIES = 103
367    PROFILE_PROGRESSION = 104
368    ALL_PROFILES = (
369        PROFILE,
370        PROFILE_INVENTORIES,
371        PROFILE_CURRENCIES,
372        PROFILE_PROGRESSION,
373    )
374    """All profile components."""
375
376    VENDORS = 400
377    VENDOR_SALES = 402
378    VENDOR_RECEIPTS = 101
379    ALL_VENDORS = (VENDORS, VENDOR_RECEIPTS, VENDOR_SALES)
380    """All vendor components."""
381
382    # Items
383    ITEM_INSTANCES = 300
384    ITEM_OBJECTIVES = 301
385    ITEM_PERKS = 302
386    ITEM_RENDER_DATA = 303
387    ITEM_STATS = 304
388    ITEM_SOCKETS = 305
389    ITEM_TALENT_GRINDS = 306
390    ITEM_PLUG_STATES = 308
391    ITEM_PLUG_OBJECTIVES = 309
392    ITEM_REUSABLE_PLUGS = 310
393
394    ALL_ITEMS = (
395        ITEM_PLUG_OBJECTIVES,
396        ITEM_PLUG_STATES,
397        ITEM_SOCKETS,
398        ITEM_INSTANCES,
399        ITEM_OBJECTIVES,
400        ITEM_PERKS,
401        ITEM_RENDER_DATA,
402        ITEM_STATS,
403        ITEM_TALENT_GRINDS,
404        ITEM_REUSABLE_PLUGS,
405    )
406    """All item components."""
407
408    PLATFORM_SILVER = 105
409    KIOSKS = 500
410    CURRENCY_LOOKUPS = 600
411    PRESENTATION_NODES = 700
412    COLLECTIBLES = 800
413    RECORDS = 900
414    TRANSITORY = 1000
415    METRICS = 1100
416    INVENTORIES = 102
417    STRING_VARIABLES = 1200
418    CRAFTABLES = 1300
419
420    CHARACTERS = 200
421    CHARACTER_INVENTORY = 201
422    CHARECTER_PROGRESSION = 202
423    CHARACTER_RENDER_DATA = 203
424    CHARACTER_ACTIVITIES = 204
425    CHARACTER_EQUIPMENT = 205
426
427    ALL_CHARACTERS = (
428        CHARACTERS,
429        CHARACTER_INVENTORY,
430        CHARECTER_PROGRESSION,
431        CHARACTER_RENDER_DATA,
432        CHARACTER_ACTIVITIES,
433        CHARACTER_EQUIPMENT,
434        RECORDS,
435    )
436    """All character components."""
437
438    ALL = (
439        *ALL_PROFILES,  # type: ignore
440        *ALL_CHARACTERS,  # type: ignore
441        *ALL_VENDORS,  # type: ignore
442        *ALL_ITEMS,  # type: ignore
443        RECORDS,
444        CURRENCY_LOOKUPS,
445        PRESENTATION_NODES,
446        COLLECTIBLES,
447        KIOSKS,
448        METRICS,
449        PLATFORM_SILVER,
450        INVENTORIES,
451        STRING_VARIABLES,
452        TRANSITORY,
453        CRAFTABLES,
454    )
455    """ALl components included."""

An Enum for Destiny 2 profile Components.

NONE = <ComponentType.NONE: 0>
PROFILE = <ComponentType.PROFILE: 100>
PROFILE_INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
PROFILE_CURRENCIES = <ComponentType.PROFILE_CURRENCIES: 103>
PROFILE_PROGRESSION = <ComponentType.PROFILE_PROGRESSION: 104>
ALL_PROFILES = <ComponentType.ALL_PROFILES: (100, 102, 103, 104)>

All profile components.

VENDORS = <ComponentType.VENDORS: 400>
VENDOR_SALES = <ComponentType.VENDOR_SALES: 402>
VENDOR_RECEIPTS = <ComponentType.VENDOR_RECEIPTS: 101>
ALL_VENDORS = <ComponentType.ALL_VENDORS: (400, 101, 402)>

All vendor components.

ITEM_INSTANCES = <ComponentType.ITEM_INSTANCES: 300>
ITEM_OBJECTIVES = <ComponentType.ITEM_OBJECTIVES: 301>
ITEM_PERKS = <ComponentType.ITEM_PERKS: 302>
ITEM_RENDER_DATA = <ComponentType.ITEM_RENDER_DATA: 303>
ITEM_STATS = <ComponentType.ITEM_STATS: 304>
ITEM_SOCKETS = <ComponentType.ITEM_SOCKETS: 305>
ITEM_TALENT_GRINDS = <ComponentType.ITEM_TALENT_GRINDS: 306>
ITEM_PLUG_STATES = <ComponentType.ITEM_PLUG_STATES: 308>
ITEM_PLUG_OBJECTIVES = <ComponentType.ITEM_PLUG_OBJECTIVES: 309>
ITEM_REUSABLE_PLUGS = <ComponentType.ITEM_REUSABLE_PLUGS: 310>
ALL_ITEMS = <ComponentType.ALL_ITEMS: (309, 308, 305, 300, 301, 302, 303, 304, 306, 310)>

All item components.

PLATFORM_SILVER = <ComponentType.PLATFORM_SILVER: 105>
KIOSKS = <ComponentType.KIOSKS: 500>
CURRENCY_LOOKUPS = <ComponentType.CURRENCY_LOOKUPS: 600>
PRESENTATION_NODES = <ComponentType.PRESENTATION_NODES: 700>
COLLECTIBLES = <ComponentType.COLLECTIBLES: 800>
RECORDS = <ComponentType.RECORDS: 900>
TRANSITORY = <ComponentType.TRANSITORY: 1000>
METRICS = <ComponentType.METRICS: 1100>
INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
STRING_VARIABLES = <ComponentType.STRING_VARIABLES: 1200>
CRAFTABLES = <ComponentType.CRAFTABLES: 1300>
CHARACTERS = <ComponentType.CHARACTERS: 200>
CHARACTER_INVENTORY = <ComponentType.CHARACTER_INVENTORY: 201>
CHARECTER_PROGRESSION = <ComponentType.CHARECTER_PROGRESSION: 202>
CHARACTER_RENDER_DATA = <ComponentType.CHARACTER_RENDER_DATA: 203>
CHARACTER_ACTIVITIES = <ComponentType.CHARACTER_ACTIVITIES: 204>
CHARACTER_EQUIPMENT = <ComponentType.CHARACTER_EQUIPMENT: 205>
ALL_CHARACTERS = <ComponentType.ALL_CHARACTERS: (200, 201, 202, 203, 204, 205, 900)>

All character components.

ALL = <ComponentType.ALL: (100, 102, 103, 104, 200, 201, 202, 203, 204, 205, 900, 400, 101, 402, 309, 308, 305, 300, 301, 302, 303, 304, 306, 310, 900, 600, 700, 800, 500, 1100, 105, 102, 1200, 1000, 1300)>

ALl components included.

Inherited Members
Enum
name
value
@typing.final
class CredentialType(builtins.int, aiobungie.Enum):
661@typing.final
662class CredentialType(int, Enum):
663    """The types of the accounts system supports at bungie."""
664
665    NONE = 0
666    XUID = 1
667    PSNID = 2
668    WILD = 3
669    FAKE = 4
670    FACEBOOK = 5
671    GOOGLE = 8
672    WINDOWS = 9
673    DEMONID = 10
674    STEAMID = 12
675    BATTLENETID = 14
676    STADIAID = 16
677    TWITCHID = 18

The types of the accounts system supports at bungie.

NONE = <CredentialType.NONE: 0>
XUID = <CredentialType.XUID: 1>
PSNID = <CredentialType.PSNID: 2>
WILD = <CredentialType.WILD: 3>
FAKE = <CredentialType.FAKE: 4>
FACEBOOK = <CredentialType.FACEBOOK: 5>
GOOGLE = <CredentialType.GOOGLE: 8>
WINDOWS = <CredentialType.WINDOWS: 9>
DEMONID = <CredentialType.DEMONID: 10>
STEAMID = <CredentialType.STEAMID: 12>
BATTLENETID = <CredentialType.BATTLENETID: 14>
STADIAID = <CredentialType.STADIAID: 16>
TWITCHID = <CredentialType.TWITCHID: 18>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class DamageType(builtins.int, aiobungie.Enum):
539@typing.final
540class DamageType(int, Enum):
541    """Enums for Destiny Damage types"""
542
543    NONE = 0
544    KINETIC = 1
545    ARC = 2
546    SOLAR = 3
547    VOID = 4
548    RAID = 5
549    """This is a special damage type reserved for some raid activity encounters."""
550    STASIS = 6

Enums for Destiny Damage types

NONE = <DamageType.NONE: 0>
KINETIC = <DamageType.KINETIC: 1>
ARC = <DamageType.ARC: 2>
SOLAR = <DamageType.SOLAR: 3>
VOID = <DamageType.VOID: 4>
RAID = <DamageType.RAID: 5>

This is a special damage type reserved for some raid activity encounters.

STASIS = <DamageType.STASIS: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Difficulty(builtins.int, aiobungie.Enum):
64@typing.final
65class Difficulty(int, enums.Enum):
66    """An enum for activities difficulties."""
67
68    TRIVIAL = 0
69    EASY = 1
70    NORMAL = 2
71    CHALLENGING = 3
72    HARD = 4
73    BRAVE = 5
74    ALMOST_IMPOSSIBLE = 6
75    IMPOSSIBLE = 7

An enum for activities difficulties.

TRIVIAL = <Difficulty.TRIVIAL: 0>
EASY = <Difficulty.EASY: 1>
NORMAL = <Difficulty.NORMAL: 2>
CHALLENGING = <Difficulty.CHALLENGING: 3>
HARD = <Difficulty.HARD: 4>
BRAVE = <Difficulty.BRAVE: 5>
ALMOST_IMPOSSIBLE = <Difficulty.ALMOST_IMPOSSIBLE: 6>
IMPOSSIBLE = <Difficulty.IMPOSSIBLE: 7>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Dungeon(builtins.int, aiobungie.Enum):
160@typing.final
161class Dungeon(int, Enum):
162    """An Enum for all available Dungeon/Like missions in Destiny 2."""
163
164    NORMAL_PRESAGE = 2124066889
165    """Normal Presage"""
166
167    MASTER_PRESAGE = 4212753278
168    """Master Presage"""
169
170    HARBINGER = 1738383283
171    """Harbinger"""
172
173    PROPHECY = 4148187374
174    """Prophecy"""
175
176    MASTER_POH = 785700673
177    """Master Pit of Heresy?"""
178
179    LEGEND_POH = 785700678
180    """Legend Pit of Heresy?"""
181
182    POH = 1375089621
183    """Normal Pit of Heresy."""
184
185    SHATTERED = 2032534090
186    """Shattered Throne"""
187
188    GOA_LEGEND = 4078656646
189    """Grasp of Avarice legend."""
190
191    GOA_MASTER = 3774021532
192    """Grasp of Avarice master."""

An Enum for all available Dungeon/Like missions in Destiny 2.

NORMAL_PRESAGE = <Dungeon.NORMAL_PRESAGE: 2124066889>

Normal Presage

MASTER_PRESAGE = <Dungeon.MASTER_PRESAGE: 4212753278>

Master Presage

HARBINGER = <Dungeon.HARBINGER: 1738383283>

Harbinger

PROPHECY = <Dungeon.PROPHECY: 4148187374>

Prophecy

MASTER_POH = <Dungeon.MASTER_POH: 785700673>

Master Pit of Heresy?

LEGEND_POH = <Dungeon.LEGEND_POH: 785700678>

Legend Pit of Heresy?

POH = <Dungeon.POH: 1375089621>

Normal Pit of Heresy.

SHATTERED = <Dungeon.SHATTERED: 2032534090>

Shattered Throne

GOA_LEGEND = <Dungeon.GOA_LEGEND: 4078656646>

Grasp of Avarice legend.

GOA_MASTER = <Dungeon.GOA_MASTER: 3774021532>

Grasp of Avarice master.

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Enum(enum.Enum):
72class Enum(__enum.Enum):
73    """Builtin Python enum with extra handlings."""
74
75    @property
76    def name(self) -> str:  # type: ignore[override]
77        return self._name_
78
79    @property
80    def value(self) -> typing.Any:  # type: ignore[override]
81        return self._value_
82
83    def __str__(self) -> str:
84        return self._name_
85
86    def __repr__(self) -> str:
87        return f"<{type(self).__name__}.{self._name_}: {self._value_!s}>"
88
89    def __int__(self) -> int:
90        if isinstance(self.value, _ITERABLE):
91            raise TypeError(
92                f"Can't overload {self.value} in {type(self).__name__}, Please use `.value` attribute.",
93            )
94        return int(self.value)

Builtin Python enum with extra handlings.

name: str

The name of the Enum member.

value: Any

The value of the Enum member.

class Factory(aiobungie.interfaces.factory.FactoryInterface):
  61class Factory(interfaces.FactoryInterface):
  62    """The base deserialization factory class for all aiobungie objects.
  63
  64    Highly inspired hikari entity factory used to deserialize JSON responses from the REST client and turning them
  65    into a `aiobungie.crates` Python classes.
  66    """
  67
  68    __slots__ = ("_net",)
  69
  70    def __init__(self, net: traits.Netrunner) -> None:
  71        self._net = net
  72
  73    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
  74        return user.BungieUser(
  75            id=int(data["membershipId"]),
  76            created_at=time.clean_date(data["firstAccess"]),
  77            name=data.get("cachedBungieGlobalDisplayName", undefined.UNDEFINED),
  78            is_deleted=data["isDeleted"],
  79            about=data["about"],
  80            updated_at=time.clean_date(data["lastUpdate"]),
  81            psn_name=data.get("psnDisplayName", None),
  82            stadia_name=data.get("stadiaDisplayName", None),
  83            steam_name=data.get("steamDisplayName", None),
  84            twitch_name=data.get("twitchDisplayName", None),
  85            blizzard_name=data.get("blizzardDisplayName", None),
  86            status=data["statusText"],
  87            locale=data["locale"],
  88            picture=assets.Image(path=str(data["profilePicturePath"])),
  89            code=data.get("cachedBungieGlobalDisplayNameCode", None),
  90            unique_name=data.get("uniqueName", None),
  91            theme_id=int(data["profileTheme"]),
  92            show_activity=bool(data["showActivity"]),
  93            theme_name=data["profileThemeName"],
  94            display_title=data["userTitleDisplay"],
  95        )
  96
  97    def deserialize_partial_bungie_user(
  98        self, payload: typedefs.JSONObject
  99    ) -> user.PartialBungieUser:
 100        return user.PartialBungieUser(
 101            net=self._net,
 102            types=[
 103                enums.MembershipType(type_)
 104                for type_ in payload.get("applicableMembershipTypes", [])
 105            ],
 106            name=payload.get("displayName", undefined.UNDEFINED),
 107            id=int(payload["membershipId"]),
 108            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 109            is_public=payload["isPublic"],
 110            icon=assets.Image(payload.get("iconPath", "")),
 111            type=enums.MembershipType(payload["membershipType"]),
 112        )
 113
 114    def deserialize_destiny_membership(
 115        self, payload: typedefs.JSONObject
 116    ) -> user.DestinyMembership:
 117        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
 118        if (
 119            raw_name := payload.get("bungieGlobalDisplayName", "")
 120        ) and not typedefs.is_unknown(raw_name):
 121            name = raw_name
 122
 123        return user.DestinyMembership(
 124            net=self._net,
 125            id=int(payload["membershipId"]),
 126            name=name,
 127            code=payload.get("bungieGlobalDisplayNameCode", None),
 128            last_seen_name=payload.get("LastSeenDisplayName")
 129            or payload.get("displayName")  # noqa: W503
 130            or "",  # noqa: W503
 131            type=enums.MembershipType(payload["membershipType"]),
 132            is_public=payload["isPublic"],
 133            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 134            icon=assets.Image(payload.get("iconPath", "")),
 135            types=[
 136                enums.MembershipType(type_)
 137                for type_ in payload.get("applicableMembershipTypes", [])
 138            ],
 139        )
 140
 141    def deserialize_destiny_memberships(
 142        self, data: typedefs.JSONArray
 143    ) -> collections.Sequence[user.DestinyMembership]:
 144        return [self.deserialize_destiny_membership(membership) for membership in data]
 145
 146    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
 147
 148        primary_membership_id: typing.Optional[int] = None
 149        if raw_primary_id := data.get("primaryMembershipId"):
 150            primary_membership_id = int(raw_primary_id)
 151
 152        return user.User(
 153            bungie=self.deserialize_bungie_user(data["bungieNetUser"]),
 154            destiny=self.deserialize_destiny_memberships(data["destinyMemberships"]),
 155            primary_membership_id=primary_membership_id,
 156        )
 157
 158    def deserialize_searched_user(
 159        self, payload: typedefs.JSONObject
 160    ) -> user.SearchableDestinyUser:
 161        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
 162        if (raw_name := payload["bungieGlobalDisplayName"]) and not typedefs.is_unknown(
 163            raw_name
 164        ):
 165            name = raw_name
 166
 167        code: typing.Optional[int] = None
 168        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
 169            code = int(raw_code)
 170
 171        bungie_id: typing.Optional[int] = None
 172        if raw_bungie_id := payload.get("bungieNetMembershipId"):
 173            bungie_id = int(raw_bungie_id)
 174
 175        return user.SearchableDestinyUser(
 176            name=name,
 177            code=code,
 178            bungie_id=bungie_id,
 179            memberships=self.deserialize_destiny_memberships(
 180                payload["destinyMemberships"]
 181            ),
 182        )
 183
 184    def deserialize_user_credentials(
 185        self, payload: typedefs.JSONArray
 186    ) -> collections.Sequence[user.UserCredentials]:
 187        return [
 188            user.UserCredentials(
 189                type=enums.CredentialType(int(creds["credentialType"])),
 190                display_name=creds["credentialDisplayName"],
 191                is_public=creds["isPublic"],
 192                self_as_string=creds.get("credentialAsString", undefined.UNDEFINED),
 193            )
 194            for creds in payload
 195        ]
 196
 197    def deserialize_user_themes(
 198        self, payload: typedefs.JSONArray
 199    ) -> collections.Sequence[user.UserThemes]:
 200        return [
 201            user.UserThemes(
 202                id=int(entry["userThemeId"]),
 203                name=entry["userThemeName"]
 204                if "userThemeName" in entry
 205                else undefined.UNDEFINED,
 206                description=entry["userThemeDescription"]
 207                if "userThemeDescription" in entry
 208                else undefined.UNDEFINED,
 209            )
 210            for entry in payload
 211        ]
 212
 213    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
 214
 215        # This is kinda redundant
 216        data = payload
 217
 218        # This is always outside the details.
 219        current_user_map: typing.Optional[
 220            collections.Mapping[str, clans.ClanMember]
 221        ] = None
 222        if raw_current_user_map := payload.get("currentUserMemberMap"):
 223            current_user_map = {
 224                membership_type: self.deserialize_clan_member(membership)
 225                for membership_type, membership in raw_current_user_map.items()
 226            }
 227
 228        try:
 229            data = payload["detail"]
 230        except KeyError:
 231            pass
 232
 233        id = data["groupId"]
 234        name = data["name"]
 235        created_at = data["creationDate"]
 236        member_count = data["memberCount"]
 237        about = data["about"]
 238        motto = data["motto"]
 239        is_public = data["isPublic"]
 240        banner = assets.Image(str(data["bannerPath"]))
 241        avatar = assets.Image(str(data["avatarPath"]))
 242        tags = data["tags"]
 243        type = data["groupType"]
 244
 245        features = data["features"]
 246        features_obj = clans.ClanFeatures(
 247            max_members=features["maximumMembers"],
 248            max_membership_types=features["maximumMembershipsOfGroupType"],
 249            capabilities=features["capabilities"],
 250            membership_types=features["membershipTypes"],
 251            invite_permissions=features["invitePermissionOverride"],
 252            update_banner_permissions=features["updateBannerPermissionOverride"],
 253            update_culture_permissions=features["updateCulturePermissionOverride"],
 254            join_level=features["joinLevel"],
 255        )
 256
 257        information: typedefs.JSONObject = data["clanInfo"]
 258        progression: collections.Mapping[int, progressions.Progression] = {
 259            int(prog_hash): self.deserialize_progressions(prog)
 260            for prog_hash, prog in information["d2ClanProgressions"].items()
 261        }
 262
 263        founder: typedefs.NoneOr[clans.ClanMember] = None
 264        if raw_founder := payload.get("founder"):
 265            founder = self.deserialize_clan_member(raw_founder)
 266
 267        return clans.Clan(
 268            net=self._net,
 269            id=int(id),
 270            name=name,
 271            type=enums.GroupType(type),
 272            created_at=time.clean_date(created_at),
 273            member_count=member_count,
 274            motto=motto,
 275            about=about,
 276            is_public=is_public,
 277            banner=banner,
 278            avatar=avatar,
 279            tags=tags,
 280            features=features_obj,
 281            owner=founder,
 282            progressions=progression,
 283            call_sign=information["clanCallsign"],
 284            banner_data=information["clanBannerData"],
 285            chat_security=data["chatSecurity"],
 286            conversation_id=int(data["conversationId"]),
 287            allow_chat=data["allowChat"],
 288            theme=data["theme"],
 289            current_user_membership=current_user_map,
 290        )
 291
 292    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
 293        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
 294        return clans.ClanMember(
 295            net=self._net,
 296            last_seen_name=destiny_user.last_seen_name,
 297            id=destiny_user.id,
 298            name=destiny_user.name,
 299            icon=destiny_user.icon,
 300            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
 301            group_id=int(data["groupId"]),
 302            joined_at=time.clean_date(data["joinDate"]),
 303            types=destiny_user.types,
 304            is_public=destiny_user.is_public,
 305            type=destiny_user.type,
 306            code=destiny_user.code,
 307            is_online=data["isOnline"],
 308            crossave_override=destiny_user.crossave_override,
 309            bungie=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
 310            if "bungieNetUserInfo" in data
 311            else None,
 312            member_type=enums.ClanMemberType(int(data["memberType"])),
 313        )
 314
 315    def deserialize_clan_members(
 316        self, data: typedefs.JSONObject, /
 317    ) -> iterators.Iterator[clans.ClanMember]:
 318        return iterators.Iterator(
 319            [self.deserialize_clan_member(member) for member in data["results"]]
 320        )
 321
 322    def deserialize_group_member(
 323        self, payload: typedefs.JSONObject
 324    ) -> clans.GroupMember:
 325        member = payload["member"]
 326        return clans.GroupMember(
 327            net=self._net,
 328            join_date=time.clean_date(member["joinDate"]),
 329            group_id=int(member["groupId"]),
 330            member_type=enums.ClanMemberType(member["memberType"]),
 331            is_online=member["isOnline"],
 332            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
 333            inactive_memberships=payload.get("areAllMembershipsInactive", None),
 334            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
 335            group=self.deserialize_clan(payload["group"]),
 336        )
 337
 338    def _deserialize_clan_conversation(
 339        self, payload: typedefs.JSONObject
 340    ) -> clans.ClanConversation:
 341        return clans.ClanConversation(
 342            net=self._net,
 343            id=int(payload["conversationId"]),
 344            group_id=int(payload["groupId"]),
 345            name=(
 346                payload["chatName"]
 347                if not typedefs.is_unknown(payload["chatName"])
 348                else undefined.UNDEFINED
 349            ),
 350            chat_enabled=payload["chatEnabled"],
 351            security=payload["chatSecurity"],
 352        )
 353
 354    def deserialize_clan_conversations(
 355        self, payload: typedefs.JSONArray
 356    ) -> collections.Sequence[clans.ClanConversation]:
 357        return [self._deserialize_clan_conversation(conv) for conv in payload]
 358
 359    def deserialize_app_owner(
 360        self, payload: typedefs.JSONObject
 361    ) -> application.ApplicationOwner:
 362        return application.ApplicationOwner(
 363            net=self._net,
 364            name=payload.get("bungieGlobalDisplayName", undefined.UNDEFINED),
 365            id=int(payload["membershipId"]),
 366            type=enums.MembershipType(payload["membershipType"]),
 367            icon=assets.Image(str(payload["iconPath"])),
 368            is_public=payload["isPublic"],
 369            code=payload.get("bungieGlobalDisplayNameCode", None),
 370        )
 371
 372    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
 373        return application.Application(
 374            id=int(payload["applicationId"]),
 375            name=payload["name"],
 376            link=payload["link"],
 377            status=payload["status"],
 378            redirect_url=payload.get("redirectUrl", None),
 379            created_at=time.clean_date(str(payload["creationDate"])),
 380            published_at=time.clean_date(str(payload["firstPublished"])),
 381            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
 382            scope=payload.get("scope", undefined.UNDEFINED),
 383        )
 384
 385    def _set_character_attrs(self, payload: typedefs.JSONObject) -> character.Character:
 386        total_time = time.format_played(int(payload["minutesPlayedTotal"]), suffix=True)
 387        return character.Character(
 388            net=self._net,
 389            id=int(payload["characterId"]),
 390            gender=enums.Gender(payload["genderType"]),
 391            race=enums.Race(payload["raceType"]),
 392            class_type=enums.Class(payload["classType"]),
 393            emblem=assets.Image(str(payload["emblemBackgroundPath"])),
 394            emblem_icon=assets.Image(str(payload["emblemPath"])),
 395            emblem_hash=int(payload["emblemHash"]),
 396            last_played=time.clean_date(payload["dateLastPlayed"]),
 397            total_played_time=total_time,
 398            member_id=int(payload["membershipId"]),
 399            member_type=enums.MembershipType(payload["membershipType"]),
 400            level=payload["baseCharacterLevel"],
 401            title_hash=payload.get("titleRecordHash", None),
 402            light=payload["light"],
 403            stats={enums.Stat(int(k)): v for k, v in payload["stats"].items()},
 404        )
 405
 406    def deserialize_profile(
 407        self, payload: typedefs.JSONObject, /
 408    ) -> typing.Optional[profile.Profile]:
 409        if (raw_profile := payload.get("data")) is None:
 410            return None
 411
 412        payload = raw_profile
 413        id = int(payload["userInfo"]["membershipId"])
 414        name = payload["userInfo"]["displayName"]
 415        is_public = payload["userInfo"]["isPublic"]
 416        type = enums.MembershipType(payload["userInfo"]["membershipType"])
 417        last_played = time.clean_date(str(payload["dateLastPlayed"]))
 418        character_ids = [int(cid) for cid in payload["characterIds"]]
 419        power_cap = payload["currentSeasonRewardPowerCap"]
 420
 421        return profile.Profile(
 422            id=int(id),
 423            name=name,
 424            is_public=is_public,
 425            type=type,
 426            last_played=last_played,
 427            character_ids=character_ids,
 428            power_cap=power_cap,
 429            net=self._net,
 430        )
 431
 432    def deserialize_profile_item(
 433        self, payload: typedefs.JSONObject
 434    ) -> profile.ProfileItemImpl:
 435
 436        instance_id: typing.Optional[int] = None
 437        if raw_instance_id := payload.get("itemInstanceId"):
 438            instance_id = int(raw_instance_id)
 439
 440        version_number: typing.Optional[int] = None
 441        if raw_version := payload.get("versionNumber"):
 442            version_number = int(raw_version)
 443
 444        transfer_status = enums.TransferStatus(payload["transferStatus"])
 445
 446        return profile.ProfileItemImpl(
 447            net=self._net,
 448            hash=payload["itemHash"],
 449            quantity=payload["quantity"],
 450            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
 451            location=enums.ItemLocation(payload["location"]),
 452            bucket=payload["bucketHash"],
 453            transfer_status=transfer_status,
 454            lockable=payload["lockable"],
 455            state=enums.ItemState(payload["state"]),
 456            dismantel_permissions=payload["dismantlePermission"],
 457            is_wrapper=payload["isWrapper"],
 458            instance_id=instance_id,
 459            version_number=version_number,
 460            ornament_id=payload.get("overrideStyleItemHash"),
 461        )
 462
 463    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
 464        return records.Objective(
 465            net=self._net,
 466            hash=payload["objectiveHash"],
 467            visible=payload["visible"],
 468            complete=payload["complete"],
 469            completion_value=payload["completionValue"],
 470            progress=payload.get("progress"),
 471            destination_hash=payload.get("destinationHash"),
 472            activity_hash=payload.get("activityHash"),
 473        )
 474
 475    def deserialize_records(
 476        self,
 477        payload: typedefs.JSONObject,
 478        scores: typing.Optional[records.RecordScores] = None,
 479        **nodes: int,
 480    ) -> records.Record:
 481        objectives: typing.Optional[list[records.Objective]] = None
 482        interval_objectives: typing.Optional[list[records.Objective]] = None
 483        record_state: typedefs.IntAnd[records.RecordState]
 484
 485        record_state = records.RecordState(payload["state"])
 486
 487        if raw_objs := payload.get("objectives"):
 488            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
 489
 490        if raw_interval_objs := payload.get("intervalObjectives"):
 491            interval_objectives = [
 492                self.deserialize_objectives(obj) for obj in raw_interval_objs
 493            ]
 494
 495        return records.Record(
 496            scores=scores,
 497            categories_node_hash=nodes.get("categories_hash", undefined.UNDEFINED),
 498            seals_node_hash=nodes.get("seals_hash", undefined.UNDEFINED),
 499            state=record_state,
 500            objectives=objectives,
 501            interval_objectives=interval_objectives,
 502            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 503            completion_times=payload.get("completedCount", None),
 504            reward_visibility=payload.get("rewardVisibilty", None),
 505        )
 506
 507    def deserialize_character_records(
 508        self,
 509        payload: typedefs.JSONObject,
 510        scores: typing.Optional[records.RecordScores] = None,
 511        record_hashes: typing.Optional[list[int]] = None,
 512    ) -> records.CharacterRecord:
 513
 514        record = self.deserialize_records(payload, scores)
 515        return records.CharacterRecord(
 516            scores=scores,
 517            categories_node_hash=record.categories_node_hash,
 518            seals_node_hash=record.seals_node_hash,
 519            state=record.state,
 520            objectives=record.objectives,
 521            interval_objectives=record.interval_objectives,
 522            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 523            completion_times=payload.get("completedCount"),
 524            reward_visibility=payload.get("rewardVisibilty"),
 525            record_hashes=record_hashes or [],
 526        )
 527
 528    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
 529        return character.Dye(
 530            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
 531        )
 532
 533    def deserialize_character_customization(
 534        self, payload: typedefs.JSONObject
 535    ) -> character.CustomizationOptions:
 536        return character.CustomizationOptions(
 537            personality=payload["personality"],
 538            face=payload["face"],
 539            skin_color=payload["skinColor"],
 540            lip_color=payload["lipColor"],
 541            eye_color=payload["eyeColor"],
 542            hair_colors=payload.get("hairColors", []),
 543            feature_colors=payload.get("featureColors", []),
 544            decal_color=payload["decalColor"],
 545            wear_helmet=payload["wearHelmet"],
 546            hair_index=payload["hairIndex"],
 547            feature_index=payload["featureIndex"],
 548            decal_index=payload["decalIndex"],
 549        )
 550
 551    def deserialize_character_minimal_equipments(
 552        self, payload: typedefs.JSONObject
 553    ) -> character.MinimalEquipments:
 554        dyes = None
 555        if raw_dyes := payload.get("dyes"):
 556            if raw_dyes:
 557                dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
 558        return character.MinimalEquipments(
 559            net=self._net, item_hash=payload["itemHash"], dyes=dyes
 560        )
 561
 562    def deserialize_character_render_data(
 563        self, payload: typedefs.JSONObject, /
 564    ) -> character.RenderedData:
 565        return character.RenderedData(
 566            net=self._net,
 567            customization=self.deserialize_character_customization(
 568                payload["customization"]
 569            ),
 570            custom_dyes=[
 571                self.deserialize_character_dye(dye)
 572                for dye in payload["customDyes"]
 573                if dye
 574            ],
 575            equipment=[
 576                self.deserialize_character_minimal_equipments(equipment)
 577                for equipment in payload["peerView"]["equipment"]
 578            ],
 579        )
 580
 581    def deserialize_available_activity(
 582        self, payload: typedefs.JSONObject
 583    ) -> activity.AvailableActivity:
 584        return activity.AvailableActivity(
 585            hash=payload["activityHash"],
 586            is_new=payload["isNew"],
 587            is_completed=payload["isCompleted"],
 588            is_visible=payload["isVisible"],
 589            display_level=payload.get("displayLevel"),
 590            recommended_light=payload.get("recommendedLight"),
 591            difficulty=activity.Difficulty(payload["difficultyTier"]),
 592            can_join=payload["canJoin"],
 593            can_lead=payload["canLead"],
 594        )
 595
 596    def deserialize_character_activity(
 597        self, payload: typedefs.JSONObject
 598    ) -> activity.CharacterActivity:
 599        current_mode: typing.Optional[enums.GameMode] = None
 600        if raw_current_mode := payload.get("currentActivityModeType"):
 601            current_mode = enums.GameMode(raw_current_mode)
 602
 603        current_mode_types: typing.Optional[collections.Sequence[enums.GameMode]] = None
 604        if raw_current_modes := payload.get("currentActivityModeTypes"):
 605            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
 606
 607        return activity.CharacterActivity(
 608            date_started=time.clean_date(payload["dateActivityStarted"]),
 609            current_hash=payload["currentActivityHash"],
 610            current_mode_hash=payload["currentActivityModeHash"],
 611            current_mode=current_mode,
 612            current_mode_hashes=payload.get("currentActivityModeHashes"),
 613            current_mode_types=current_mode_types,
 614            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
 615            last_story_hash=payload["lastCompletedStoryHash"],
 616            available_activities=[
 617                self.deserialize_available_activity(activity_)
 618                for activity_ in payload["availableActivities"]
 619            ],
 620        )
 621
 622    def deserialize_profile_items(
 623        self, payload: typedefs.JSONObject, /
 624    ) -> list[profile.ProfileItemImpl]:
 625        return [self.deserialize_profile_item(item) for item in payload["items"]]
 626
 627    def _deserialize_node(self, payload: typedefs.JSONObject) -> records.Node:
 628        return records.Node(
 629            state=int(payload["state"]),
 630            objective=self.deserialize_objectives(payload["objective"])
 631            if "objective" in payload
 632            else None,
 633            progress_value=int(payload["progressValue"]),
 634            completion_value=int(payload["completionValue"]),
 635            record_category_score=int(payload["recordCategoryScore"])
 636            if "recordCategoryScore" in payload
 637            else None,
 638        )
 639
 640    @staticmethod
 641    def _deserialize_collectible(payload: typedefs.JSONObject) -> items.Collectible:
 642        recent_collectibles: typing.Optional[collections.Collection[int]] = None
 643        if raw_recent_collectibles := payload.get("recentCollectibleHashes"):
 644            recent_collectibles = [
 645                int(item_hash) for item_hash in raw_recent_collectibles
 646            ]
 647
 648        collectibles: dict[int, int] = {}
 649        for item_hash, mapping in payload["collectibles"].items():
 650            collectibles[int(item_hash)] = int(mapping["state"])
 651
 652        return items.Collectible(
 653            recent_collectibles=recent_collectibles,
 654            collectibles=collectibles,
 655            collection_categorie_hash=int(payload["collectionCategoriesRootNodeHash"]),
 656            collection_badges_hash=int(payload["collectionBadgesRootNodeHash"]),
 657        )
 658
 659    @staticmethod
 660    def _deserialize_currencies(
 661        payload: typedefs.JSONObject,
 662    ) -> collections.Sequence[items.Currency]:
 663        return [
 664            items.Currency(hash=int(item_hash), amount=int(amount))
 665            for item_hash, amount in payload["itemQuantities"].items()
 666        ]
 667
 668    def deserialize_progressions(
 669        self, payload: typedefs.JSONObject
 670    ) -> progressions.Progression:
 671        return progressions.Progression(
 672            hash=int(payload["progressionHash"]),
 673            level=int(payload["level"]),
 674            cap=int(payload["levelCap"]),
 675            daily_limit=int(payload["dailyLimit"]),
 676            weekly_limit=int(payload["weeklyLimit"]),
 677            current_progress=int(payload["currentProgress"]),
 678            daily_progress=int(payload["dailyProgress"]),
 679            needed=int(payload["progressToNextLevel"]),
 680            next_level=int(payload["nextLevelAt"]),
 681        )
 682
 683    def _deserialize_factions(
 684        self, payload: typedefs.JSONObject
 685    ) -> progressions.Factions:
 686        progs = self.deserialize_progressions(payload)
 687        return progressions.Factions(
 688            hash=progs.hash,
 689            level=progs.level,
 690            cap=progs.cap,
 691            daily_limit=progs.daily_limit,
 692            weekly_limit=progs.weekly_limit,
 693            current_progress=progs.current_progress,
 694            daily_progress=progs.daily_progress,
 695            needed=progs.needed,
 696            next_level=progs.next_level,
 697            faction_hash=payload["factionHash"],
 698            faction_vendor_hash=payload["factionVendorIndex"],
 699        )
 700
 701    def _deserialize_milestone_available_quest(
 702        self, payload: typedefs.JSONObject
 703    ) -> milestones.MilestoneQuest:
 704        return milestones.MilestoneQuest(
 705            item_hash=payload["questItemHash"],
 706            status=self._deserialize_milestone_quest_status(payload["status"]),
 707        )
 708
 709    def _deserialize_milestone_activity(
 710        self, payload: typedefs.JSONObject
 711    ) -> milestones.MilestoneActivity:
 712
 713        phases: typing.Optional[
 714            collections.Sequence[milestones.MilestoneActivityPhase]
 715        ] = None
 716        if raw_phases := payload.get("phases"):
 717            phases = [
 718                milestones.MilestoneActivityPhase(
 719                    is_completed=obj["complete"], hash=obj["phaseHash"]
 720                )
 721                for obj in raw_phases
 722            ]
 723
 724        return milestones.MilestoneActivity(
 725            hash=payload["activityHash"],
 726            challenges=[
 727                self.deserialize_objectives(obj["objective"])
 728                for obj in payload["challenges"]
 729            ],
 730            modifier_hashes=payload.get("modifierHashes"),
 731            boolean_options=payload.get("booleanActivityOptions"),
 732            phases=phases,
 733        )
 734
 735    def _deserialize_milestone_quest_status(
 736        self, payload: typedefs.JSONObject
 737    ) -> milestones.QuestStatus:
 738        return milestones.QuestStatus(
 739            net=self._net,
 740            quest_hash=payload["questHash"],
 741            step_hash=payload["stepHash"],
 742            step_objectives=[
 743                self.deserialize_objectives(objective)
 744                for objective in payload["stepObjectives"]
 745            ],
 746            is_tracked=payload["tracked"],
 747            is_completed=payload["completed"],
 748            started=payload["started"],
 749            item_instance_id=payload["itemInstanceId"],
 750            vendor_hash=payload.get("vendorHash"),
 751            is_redeemed=payload["redeemed"],
 752        )
 753
 754    def _deserialize_milestone_rewards(
 755        self, payload: typedefs.JSONObject
 756    ) -> milestones.MilestoneReward:
 757        return milestones.MilestoneReward(
 758            category_hash=payload["rewardCategoryHash"],
 759            entries=[
 760                milestones.MilestoneRewardEntry(
 761                    entry_hash=entry["rewardEntryHash"],
 762                    is_earned=entry["earned"],
 763                    is_redeemed=entry["redeemed"],
 764                )
 765                for entry in payload["entries"]
 766            ],
 767        )
 768
 769    def deserialize_milestone(
 770        self, payload: typedefs.JSONObject
 771    ) -> milestones.Milestone:
 772        start_date: typing.Optional[datetime.datetime] = None
 773        if raw_start_date := payload.get("startDate"):
 774            start_date = time.clean_date(raw_start_date)
 775
 776        end_date: typing.Optional[datetime.datetime] = None
 777        if raw_end_date := payload.get("endDate"):
 778            end_date = time.clean_date(raw_end_date)
 779
 780        rewards: typing.Optional[
 781            collections.Collection[milestones.MilestoneReward]
 782        ] = None
 783        if raw_rewards := payload.get("rewards"):
 784            rewards = [
 785                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
 786            ]
 787
 788        activities: typing.Optional[
 789            collections.Sequence[milestones.MilestoneActivity]
 790        ] = None
 791        if raw_activities := payload.get("activities"):
 792            activities = [
 793                self._deserialize_milestone_activity(active)
 794                for active in raw_activities
 795            ]
 796
 797        quests: typing.Optional[collections.Sequence[milestones.MilestoneQuest]] = None
 798        if raw_quests := payload.get("availableQuests"):
 799            quests = [
 800                self._deserialize_milestone_available_quest(quest)
 801                for quest in raw_quests
 802            ]
 803
 804        vendors: typing.Optional[
 805            collections.Sequence[milestones.MilestoneVendor]
 806        ] = None
 807        if raw_vendors := payload.get("vendors"):
 808            vendors = [
 809                milestones.MilestoneVendor(
 810                    vendor_hash=vendor["vendorHash"],
 811                    preview_itemhash=vendor.get("previewItemHash"),
 812                )
 813                for vendor in raw_vendors
 814            ]
 815
 816        return milestones.Milestone(
 817            hash=payload["milestoneHash"],
 818            start_date=start_date,
 819            end_date=end_date,
 820            order=payload["order"],
 821            rewards=rewards,
 822            available_quests=quests,
 823            activities=activities,
 824            vendors=vendors,
 825        )
 826
 827    def _deserialize_artifact_tiers(
 828        self, payload: typedefs.JSONObject
 829    ) -> season.ArtifactTier:
 830        return season.ArtifactTier(
 831            hash=payload["tierHash"],
 832            is_unlocked=payload["isUnlocked"],
 833            points_to_unlock=payload["pointsToUnlock"],
 834            items=[
 835                season.ArtifactTierItem(
 836                    hash=item["itemHash"], is_active=item["isActive"]
 837                )
 838                for item in payload["items"]
 839            ],
 840        )
 841
 842    def deserialize_characters(
 843        self, payload: typedefs.JSONObject
 844    ) -> collections.Mapping[int, character.Character]:
 845        return {
 846            int(char_id): self._set_character_attrs(char)
 847            for char_id, char in payload["data"].items()
 848        }
 849
 850    def deserialize_character(
 851        self, payload: typedefs.JSONObject
 852    ) -> character.Character:
 853        return self._set_character_attrs(payload)
 854
 855    def deserialize_character_equipments(
 856        self, payload: typedefs.JSONObject
 857    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
 858        return {
 859            int(char_id): self.deserialize_profile_items(item)
 860            for char_id, item in payload["data"].items()
 861        }
 862
 863    def deserialize_character_activities(
 864        self, payload: typedefs.JSONObject
 865    ) -> collections.Mapping[int, activity.CharacterActivity]:
 866        return {
 867            int(char_id): self.deserialize_character_activity(data)
 868            for char_id, data in payload["data"].items()
 869        }
 870
 871    def deserialize_characters_render_data(
 872        self, payload: typedefs.JSONObject
 873    ) -> collections.Mapping[int, character.RenderedData]:
 874        return {
 875            int(char_id): self.deserialize_character_render_data(data)
 876            for char_id, data in payload["data"].items()
 877        }
 878
 879    def deserialize_character_progressions(
 880        self, payload: typedefs.JSONObject
 881    ) -> character.CharacterProgression:
 882        progressions_ = {
 883            int(prog_id): self.deserialize_progressions(prog)
 884            for prog_id, prog in payload["progressions"].items()
 885        }
 886
 887        factions = {
 888            int(faction_id): self._deserialize_factions(faction)
 889            for faction_id, faction in payload["factions"].items()
 890        }
 891
 892        milestones_ = {
 893            int(milestone_hash): self.deserialize_milestone(milestone)
 894            for milestone_hash, milestone in payload["milestones"].items()
 895        }
 896
 897        uninstanced_item_objectives = {
 898            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
 899            for item_hash, obj in payload["uninstancedItemObjectives"].items()
 900        }
 901
 902        artifact = payload["seasonalArtifact"]
 903        seasonal_artifact = season.CharacterScopedArtifact(
 904            hash=artifact["artifactHash"],
 905            points_used=artifact["pointsUsed"],
 906            reset_count=artifact["resetCount"],
 907            tiers=[
 908                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
 909            ],
 910        )
 911        checklists = payload["checklists"]
 912
 913        return character.CharacterProgression(
 914            progressions=progressions_,
 915            factions=factions,
 916            checklists=checklists,
 917            milestones=milestones_,
 918            seasonal_artifact=seasonal_artifact,
 919            uninstanced_item_objectives=uninstanced_item_objectives,
 920        )
 921
 922    def deserialize_character_progressions_mapping(
 923        self, payload: typedefs.JSONObject
 924    ) -> collections.Mapping[int, character.CharacterProgression]:
 925        character_progressions: collections.Mapping[
 926            int, character.CharacterProgression
 927        ] = {}
 928        for char_id, data in payload["data"].items():
 929            # A little hack to stop mypy complaining about Mapping <-> dict
 930            character_progressions[int(char_id)] = self.deserialize_character_progressions(data)  # type: ignore[index]
 931        return character_progressions
 932
 933    def deserialize_characters_records(
 934        self,
 935        payload: typedefs.JSONObject,
 936    ) -> collections.Mapping[int, records.CharacterRecord]:
 937
 938        return {
 939            int(rec_id): self.deserialize_character_records(
 940                rec, record_hashes=payload.get("featuredRecordHashes")
 941            )
 942            for rec_id, rec in payload["records"].items()
 943        }
 944
 945    def deserialize_profile_records(
 946        self, payload: typedefs.JSONObject
 947    ) -> collections.Mapping[int, records.Record]:
 948        raw_profile_records = payload["data"]
 949        scores = records.RecordScores(
 950            current_score=raw_profile_records["score"],
 951            legacy_score=raw_profile_records["legacyScore"],
 952            lifetime_score=raw_profile_records["lifetimeScore"],
 953        )
 954        return {
 955            int(record_id): self.deserialize_records(
 956                record,
 957                scores,
 958                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
 959                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
 960            )
 961            for record_id, record in raw_profile_records["records"].items()
 962        }
 963
 964    def _deserialize_craftable_socket_plug(
 965        self, payload: typedefs.JSONObject
 966    ) -> items.CraftableSocketPlug:
 967        return items.CraftableSocketPlug(
 968            item_hash=int(payload["plugItemHash"]),
 969            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 970        )
 971
 972    def _deserialize_craftable_socket(
 973        self, payload: typedefs.JSONObject
 974    ) -> items.CraftableSocket:
 975
 976        plugs: list[items.CraftableSocketPlug] = []
 977        if raw_plug := payload.get("plug"):
 978            plugs.extend(
 979                self._deserialize_craftable_socket_plug(plug) for plug in raw_plug
 980            )
 981
 982        return items.CraftableSocket(
 983            plug_set_hash=int(payload["plugSetHash"]), plugs=plugs
 984        )
 985
 986    def _deserialize_craftable_item(
 987        self, payload: typedefs.JSONObject
 988    ) -> items.CraftableItem:
 989
 990        return items.CraftableItem(
 991            is_visible=payload["visible"],
 992            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 993            sockets=[
 994                self._deserialize_craftable_socket(socket)
 995                for socket in payload["sockets"]
 996            ],
 997        )
 998
 999    def deserialize_craftables_component(
1000        self, payload: typedefs.JSONObject
1001    ) -> components.CraftablesComponent:
1002        return components.CraftablesComponent(
1003            net=self._net,
1004            craftables={
1005                int(item_id): self._deserialize_craftable_item(item)
1006                for item_id, item in payload["craftables"].items()
1007                if item is not None
1008            },
1009            crafting_root_node_hash=payload["craftingRootNodeHash"],
1010        )
1011
1012    def deserialize_components(  # noqa: C901 Too complex.
1013        self, payload: typedefs.JSONObject
1014    ) -> components.Component:
1015
1016        profile_: typing.Optional[profile.Profile] = None
1017        if raw_profile := payload.get("profile"):
1018            profile_ = self.deserialize_profile(raw_profile)
1019
1020        profile_progression: typing.Optional[profile.ProfileProgression] = None
1021        if raw_profile_progression := payload.get("profileProgression"):
1022            profile_progression = self.deserialize_profile_progression(
1023                raw_profile_progression
1024            )
1025
1026        profile_currencies: typing.Optional[
1027            collections.Sequence[profile.ProfileItemImpl]
1028        ] = None
1029        if raw_profile_currencies := payload.get("profileCurrencies"):
1030            if "data" in raw_profile_currencies:
1031                profile_currencies = self.deserialize_profile_items(
1032                    raw_profile_currencies["data"]
1033                )
1034
1035        profile_inventories: typing.Optional[
1036            collections.Sequence[profile.ProfileItemImpl]
1037        ] = None
1038        if raw_profile_inventories := payload.get("profileInventory"):
1039            if "data" in raw_profile_inventories:
1040                profile_inventories = self.deserialize_profile_items(
1041                    raw_profile_inventories["data"]
1042                )
1043
1044        profile_records: typing.Optional[
1045            collections.Mapping[int, records.Record]
1046        ] = None
1047
1048        if raw_profile_records_ := payload.get("profileRecords"):
1049            profile_records = self.deserialize_profile_records(raw_profile_records_)
1050
1051        characters: typing.Optional[typing.Mapping[int, character.Character]] = None
1052        if raw_characters := payload.get("characters"):
1053            characters = self.deserialize_characters(raw_characters)
1054
1055        character_records: typing.Optional[
1056            collections.Mapping[int, records.CharacterRecord]
1057        ] = None
1058
1059        if raw_character_records := payload.get("characterRecords"):
1060            # Had to do it in two steps..
1061            to_update: typedefs.JSONObject = {}
1062            for _, data in raw_character_records["data"].items():
1063                for record_id, record in data.items():
1064                    to_update[record_id] = record
1065
1066            character_records = {
1067                int(rec_id): self.deserialize_character_records(
1068                    rec, record_hashes=to_update.get("featuredRecordHashes")
1069                )
1070                for rec_id, rec in to_update["records"].items()
1071            }
1072
1073        character_equipments: typing.Optional[
1074            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1075        ] = None
1076        if raw_character_equips := payload.get("characterEquipment"):
1077            character_equipments = self.deserialize_character_equipments(
1078                raw_character_equips
1079            )
1080
1081        character_inventories: typing.Optional[
1082            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1083        ] = None
1084        if raw_character_inventories := payload.get("characterInventories"):
1085            if "data" in raw_character_inventories:
1086                character_inventories = self.deserialize_character_equipments(
1087                    raw_character_inventories
1088                )
1089
1090        character_activities: typing.Optional[
1091            collections.Mapping[int, activity.CharacterActivity]
1092        ] = None
1093        if raw_char_acts := payload.get("characterActivities"):
1094            character_activities = self.deserialize_character_activities(raw_char_acts)
1095
1096        character_render_data: typing.Optional[
1097            collections.Mapping[int, character.RenderedData]
1098        ] = None
1099        if raw_character_render_data := payload.get("characterRenderData"):
1100            character_render_data = self.deserialize_characters_render_data(
1101                raw_character_render_data
1102            )
1103
1104        character_progressions: typing.Optional[
1105            collections.Mapping[int, character.CharacterProgression]
1106        ] = None
1107
1108        if raw_character_progressions := payload.get("characterProgressions"):
1109            character_progressions = self.deserialize_character_progressions_mapping(
1110                raw_character_progressions
1111            )
1112
1113        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1114        if raw_profile_string_vars := payload.get("profileStringVariables"):
1115            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1116
1117        character_string_vars: typing.Optional[
1118            collections.Mapping[int, collections.Mapping[int, int]]
1119        ] = None
1120        if raw_character_string_vars := payload.get("characterStringVariables"):
1121            character_string_vars = {
1122                int(char_id): data["integerValuesByHash"]
1123                for char_id, data in raw_character_string_vars["data"].items()
1124            }
1125
1126        metrics: typing.Optional[
1127            collections.Sequence[
1128                collections.Mapping[
1129                    int, tuple[bool, typing.Optional[records.Objective]]
1130                ]
1131            ]
1132        ] = None
1133        root_node_hash: typing.Optional[int] = None
1134
1135        if raw_metrics := payload.get("metrics"):
1136            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1137            metrics = [
1138                {
1139                    int(metrics_hash): (
1140                        data["invisible"],
1141                        self.deserialize_objectives(data["objectiveProgress"])
1142                        if "objectiveProgress" in data
1143                        else None,
1144                    )
1145                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1146                }
1147            ]
1148        transitory: typing.Optional[fireteams.FireteamParty] = None
1149        if raw_transitory := payload.get("profileTransitoryData"):
1150            if "data" in raw_transitory:
1151                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1152
1153        item_components: typing.Optional[components.ItemsComponent] = None
1154        if raw_item_components := payload.get("itemComponents"):
1155            item_components = self.deserialize_items_component(raw_item_components)
1156
1157        profile_plugsets: typing.Optional[
1158            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1159        ] = None
1160
1161        if raw_profile_plugs := payload.get("profilePlugSets"):
1162            profile_plugsets = {
1163                int(index): [self.deserialize_plug_item_state(state) for state in data]
1164                for index, data in raw_profile_plugs["data"]["plugs"].items()
1165            }
1166
1167        character_plugsets: typing.Optional[
1168            collections.Mapping[
1169                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1170            ]
1171        ] = None
1172        if raw_char_plugsets := payload.get("characterPlugSets"):
1173            character_plugsets = {
1174                int(char_id): {
1175                    int(index): [
1176                        self.deserialize_plug_item_state(state) for state in data
1177                    ]
1178                    for index, data in inner["plugs"].items()
1179                }
1180                for char_id, inner in raw_char_plugsets["data"].items()
1181            }
1182
1183        character_collectibles: typing.Optional[
1184            collections.Mapping[int, items.Collectible]
1185        ] = None
1186        if raw_character_collectibles := payload.get("characterCollectibles"):
1187            character_collectibles = {
1188                int(char_id): self._deserialize_collectible(data)
1189                for char_id, data in raw_character_collectibles["data"].items()
1190            }
1191
1192        profile_collectibles: typing.Optional[items.Collectible] = None
1193        if raw_profile_collectibles := payload.get("profileCollectibles"):
1194            profile_collectibles = self._deserialize_collectible(
1195                raw_profile_collectibles["data"]
1196            )
1197
1198        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1199        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1200            profile_nodes = {
1201                int(node_hash): self._deserialize_node(node)
1202                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1203            }
1204
1205        character_nodes: typing.Optional[
1206            collections.Mapping[int, collections.Mapping[int, records.Node]]
1207        ] = None
1208        if raw_character_nodes := payload.get("characterPresentationNodes"):
1209            character_nodes = {
1210                int(char_id): {
1211                    int(node_hash): self._deserialize_node(node)
1212                    for node_hash, node in each_character["nodes"].items()
1213                }
1214                for char_id, each_character in raw_character_nodes["data"].items()
1215            }
1216
1217        platform_silver: typing.Optional[
1218            collections.Mapping[str, profile.ProfileItemImpl]
1219        ] = None
1220        if raw_platform_silver := payload.get("platformSilver"):
1221            if "data" in raw_platform_silver:
1222                platform_silver = {
1223                    platform_name: self.deserialize_profile_item(item)
1224                    for platform_name, item in raw_platform_silver["data"][
1225                        "platformSilver"
1226                    ].items()
1227                }
1228
1229        character_currency_lookups: typing.Optional[
1230            collections.Mapping[int, collections.Sequence[items.Currency]]
1231        ] = None
1232        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1233            if "data" in raw_char_lookups:
1234                character_currency_lookups = {
1235                    int(char_id): self._deserialize_currencies(currencie)
1236                    for char_id, currencie in raw_char_lookups["data"].items()
1237                }
1238
1239        character_craftables: typing.Optional[
1240            collections.Mapping[int, components.CraftablesComponent]
1241        ] = None
1242        if raw_character_craftables := payload.get("characterCraftables"):
1243
1244            if "data" in raw_character_craftables:
1245                character_craftables = {
1246                    int(char_id): self.deserialize_craftables_component(craftable)
1247                    for char_id, craftable in raw_character_craftables["data"].items()
1248                }
1249
1250        return components.Component(
1251            profiles=profile_,
1252            profile_progression=profile_progression,
1253            profile_currencies=profile_currencies,
1254            profile_inventories=profile_inventories,
1255            profile_records=profile_records,
1256            characters=characters,
1257            character_records=character_records,
1258            character_equipments=character_equipments,
1259            character_inventories=character_inventories,
1260            character_activities=character_activities,
1261            character_render_data=character_render_data,
1262            character_progressions=character_progressions,
1263            profile_string_variables=profile_string_vars,
1264            character_string_variables=character_string_vars,
1265            metrics=metrics,
1266            root_node_hash=root_node_hash,
1267            transitory=transitory,
1268            item_components=item_components,
1269            profile_plugsets=profile_plugsets,
1270            character_plugsets=character_plugsets,
1271            character_collectibles=character_collectibles,
1272            profile_collectibles=profile_collectibles,
1273            profile_nodes=profile_nodes,
1274            character_nodes=character_nodes,
1275            platform_silver=platform_silver,
1276            character_currency_lookups=character_currency_lookups,
1277            character_craftables=character_craftables,
1278        )
1279
1280    def deserialize_items_component(
1281        self, payload: typedefs.JSONObject
1282    ) -> components.ItemsComponent:
1283        instances: typing.Optional[
1284            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1285        ] = None
1286        if raw_instances := payload.get("instances"):
1287            instances = [
1288                {
1289                    int(ins_id): self.deserialize_instanced_item(item)
1290                    for ins_id, item in raw_instances["data"].items()
1291                }
1292            ]
1293
1294        render_data: typing.Optional[
1295            collections.Mapping[int, tuple[bool, dict[int, int]]]
1296        ] = None
1297        if raw_render_data := payload.get("renderData"):
1298            render_data = {
1299                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1300                for ins_id, data in raw_render_data["data"].items()
1301            }
1302
1303        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1304        if raw_stats := payload.get("stats"):
1305            builder: collections.Mapping[int, items.ItemStatsView] = {}
1306            for ins_id, stat in raw_stats["data"].items():
1307                for _, items_ in stat.items():
1308                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1309            stats = builder
1310
1311        sockets: typing.Optional[
1312            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1313        ] = None
1314        if raw_sockets := payload.get("sockets"):
1315            sockets = {
1316                int(ins_id): [
1317                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1318                ]
1319                for ins_id, item in raw_sockets["data"].items()
1320            }
1321
1322        objeectives: typing.Optional[
1323            collections.Mapping[int, collections.Sequence[records.Objective]]
1324        ] = None
1325        if raw_objectives := payload.get("objectives"):
1326            objeectives = {
1327                int(ins_id): [self.deserialize_objectives(objective)]
1328                for ins_id, data in raw_objectives["data"].items()
1329                for objective in data["objectives"]
1330            }
1331
1332        perks: typing.Optional[
1333            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1334        ] = None
1335        if raw_perks := payload.get("perks"):
1336            perks = {
1337                int(ins_id): [
1338                    self.deserialize_item_perk(perk) for perk in item["perks"]
1339                ]
1340                for ins_id, item in raw_perks["data"].items()
1341            }
1342
1343        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1344        if raw_plug_states := payload.get("plugStates"):
1345            pending_states: list[items.PlugItemState] = []
1346            for _, plug in raw_plug_states["data"].items():
1347                pending_states.append(self.deserialize_plug_item_state(plug))
1348            plug_states = pending_states
1349
1350        reusable_plugs: typing.Optional[
1351            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1352        ] = None
1353        if raw_re_plugs := payload.get("reusablePlugs"):
1354            reusable_plugs = {
1355                int(ins_id): [
1356                    self.deserialize_plug_item_state(state) for state in inner
1357                ]
1358                for ins_id, plug in raw_re_plugs["data"].items()
1359                for inner in list(plug["plugs"].values())
1360            }
1361
1362        plug_objectives: typing.Optional[
1363            collections.Mapping[
1364                int, collections.Mapping[int, collections.Collection[records.Objective]]
1365            ]
1366        ] = None
1367        if raw_plug_objectives := payload.get("plugObjectives"):
1368            plug_objectives = {
1369                int(ins_id): {
1370                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1371                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1372                }
1373                for ins_id, inner in raw_plug_objectives["data"].items()
1374            }
1375
1376        return components.ItemsComponent(
1377            sockets=sockets,
1378            stats=stats,
1379            render_data=render_data,
1380            instances=instances,
1381            objectives=objeectives,
1382            perks=perks,
1383            plug_states=plug_states,
1384            reusable_plugs=reusable_plugs,
1385            plug_objectives=plug_objectives,
1386        )
1387
1388    def deserialize_character_component(  # type: ignore[call-arg]
1389        self, payload: typedefs.JSONObject
1390    ) -> components.CharacterComponent:
1391
1392        character_: typing.Optional[character.Character] = None
1393        if raw_singuler_character := payload.get("character"):
1394            character_ = self.deserialize_character(raw_singuler_character["data"])
1395
1396        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1397        if raw_inventory := payload.get("inventory"):
1398            if "data" in raw_inventory:
1399                inventory = self.deserialize_profile_items(raw_inventory["data"])
1400
1401        activities: typing.Optional[activity.CharacterActivity] = None
1402        if raw_activities := payload.get("activities"):
1403            activities = self.deserialize_character_activity(raw_activities["data"])
1404
1405        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1406        if raw_equipments := payload.get("equipment"):
1407            equipment = self.deserialize_profile_items(raw_equipments["data"])
1408
1409        progressions_: typing.Optional[character.CharacterProgression] = None
1410        if raw_progressions := payload.get("progressions"):
1411            progressions_ = self.deserialize_character_progressions(
1412                raw_progressions["data"]
1413            )
1414
1415        render_data: typing.Optional[character.RenderedData] = None
1416        if raw_render_data := payload.get("renderData"):
1417            render_data = self.deserialize_character_render_data(
1418                raw_render_data["data"]
1419            )
1420
1421        character_records: typing.Optional[
1422            collections.Mapping[int, records.CharacterRecord]
1423        ] = None
1424        if raw_char_records := payload.get("records"):
1425            character_records = self.deserialize_characters_records(
1426                raw_char_records["data"]
1427            )
1428
1429        item_components: typing.Optional[components.ItemsComponent] = None
1430        if raw_item_components := payload.get("itemComponents"):
1431            item_components = self.deserialize_items_component(raw_item_components)
1432
1433        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1434        if raw_nodes := payload.get("presentationNodes"):
1435            nodes = {
1436                int(node_hash): self._deserialize_node(node)
1437                for node_hash, node in raw_nodes["data"]["nodes"].items()
1438            }
1439
1440        collectibles: typing.Optional[items.Collectible] = None
1441        if raw_collectibles := payload.get("collectibles"):
1442            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1443
1444        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1445        if raw_currencies := payload.get("currencyLookups"):
1446            if "data" in raw_currencies:
1447                currency_lookups = self._deserialize_currencies(raw_currencies)
1448
1449        return components.CharacterComponent(
1450            activities=activities,
1451            equipment=equipment,
1452            inventory=inventory,
1453            progressions=progressions_,
1454            render_data=render_data,
1455            character=character_,
1456            character_records=character_records,
1457            profile_records=None,
1458            item_components=item_components,
1459            currency_lookups=currency_lookups,
1460            collectibles=collectibles,
1461            nodes=nodes,
1462        )
1463
1464    def _set_entity_attrs(
1465        self, payload: typedefs.JSONObject, *, key: str = "displayProperties"
1466    ) -> entity.Entity:
1467
1468        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1469        description: undefined.UndefinedOr[str] = undefined.UNDEFINED
1470
1471        if properties := payload[key]:
1472            if (raw_name := properties["name"]) is not typedefs.Unknown:
1473                name = raw_name
1474
1475            if (
1476                raw_description := properties["description"]
1477            ) and not typedefs.is_unknown(raw_description):
1478                description = raw_description
1479
1480        return entity.Entity(
1481            net=self._net,
1482            hash=payload["hash"],
1483            index=payload["index"],
1484            name=name,
1485            description=description,
1486            has_icon=properties["hasIcon"],
1487            icon=assets.Image(properties["icon"] if "icon" in properties else None),
1488        )
1489
1490    def deserialize_inventory_results(
1491        self, payload: typedefs.JSONObject
1492    ) -> iterators.Iterator[entity.SearchableEntity]:
1493        suggested_words: list[str] = payload["suggestedWords"]
1494
1495        def _check_unknown(s: str) -> undefined.UndefinedOr[str]:
1496            return s if not typedefs.is_unknown(s) else undefined.UNDEFINED
1497
1498        return iterators.Iterator(
1499            [
1500                entity.SearchableEntity(
1501                    net=self._net,
1502                    hash=data["hash"],
1503                    entity_type=data["entityType"],
1504                    weight=data["weight"],
1505                    suggested_words=suggested_words,
1506                    name=data["displayProperties"]["name"],
1507                    has_icon=data["displayProperties"]["hasIcon"],
1508                    description=_check_unknown(
1509                        data["displayProperties"]["description"]
1510                    ),
1511                    icon=assets.Image(data["displayProperties"]["icon"]),
1512                )
1513                for data in payload["results"]["results"]
1514            ]
1515        )
1516
1517    def _deserialize_inventory_item_objects(
1518        self, payload: typedefs.JSONObject
1519    ) -> entity.InventoryEntityObjects:
1520        return entity.InventoryEntityObjects(
1521            action=payload.get("action"),
1522            set_data=payload.get("setData"),
1523            stats=payload.get("stats"),
1524            equipping_block=payload.get("equippingBlock"),
1525            translation_block=payload.get("translationBlock"),
1526            preview=payload.get("preview"),
1527            quality=payload.get("quality"),
1528            value=payload.get("value"),
1529            source_data=payload.get("sourceData"),
1530            objectives=payload.get("objectives"),
1531            plug=payload.get("plug"),
1532            metrics=payload.get("metrics"),
1533            gearset=payload.get("gearset"),
1534            sack=payload.get("sack"),
1535            sockets=payload.get("sockets"),
1536            summary=payload.get("summary"),
1537            talent_gird=payload.get("talentGrid"),
1538            investments_stats=payload.get("investmentStats"),
1539            perks=payload.get("perks"),
1540            animations=payload.get("animations", []),
1541            links=payload.get("links", []),
1542        )
1543
1544    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1545        self, payload: typedefs.JSONObject, /
1546    ) -> entity.InventoryEntity:
1547
1548        props = self._set_entity_attrs(payload)
1549        objects = self._deserialize_inventory_item_objects(payload)
1550
1551        collectible_hash: typing.Optional[int] = None
1552        if raw_collectible_hash := payload.get("collectibleHash"):
1553            collectible_hash = int(raw_collectible_hash)
1554
1555        secondary_icon: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1556        if raw_second_icon := payload.get("secondaryIcon"):
1557            secondary_icon = assets.Image(raw_second_icon)
1558
1559        secondary_overlay: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1560        if raw_second_overlay := payload.get("secondaryOverlay"):
1561            secondary_overlay = assets.Image(raw_second_overlay)
1562
1563        secondary_special: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1564        if raw_second_special := payload.get("secondarySpecial"):
1565            secondary_special = assets.Image(raw_second_special)
1566
1567        screenshot: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1568        if raw_screenshot := payload.get("screenshot"):
1569            screenshot = assets.Image(raw_screenshot)
1570
1571        watermark_icon: typing.Optional[assets.Image] = None
1572        if raw_watermark_icon := payload.get("iconWatermark"):
1573            watermark_icon = assets.Image(raw_watermark_icon)
1574
1575        watermark_shelved: typing.Optional[assets.Image] = None
1576        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1577            watermark_shelved = assets.Image(raw_watermark_shelved)
1578
1579        about: undefined.UndefinedOr[str] = undefined.UNDEFINED
1580        if (raw_about := payload.get("flavorText")) and not typedefs.is_unknown(
1581            raw_about
1582        ):
1583            about = raw_about
1584
1585        ui_item_style: undefined.UndefinedOr[str] = undefined.UNDEFINED
1586        if (
1587            raw_ui_style := payload.get("uiItemDisplayStyle")
1588        ) and not typedefs.is_unknown(raw_ui_style):
1589            ui_item_style = raw_ui_style
1590
1591        tier_and_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1592        if (
1593            raw_tier_and_name := payload.get("itemTypeAndTierDisplayName")
1594        ) and not typedefs.is_unknown(raw_tier_and_name):
1595            tier_and_name = raw_tier_and_name
1596
1597        type_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1598        if (
1599            raw_type_name := payload.get("itemTypeDisplayName")
1600        ) and not typedefs.is_unknown(raw_type_name):
1601            type_name = raw_type_name
1602
1603        display_source: undefined.UndefinedOr[str] = undefined.UNDEFINED
1604        if (
1605            raw_display_source := payload.get("displaySource")
1606        ) and not typedefs.is_unknown(raw_display_source):
1607            display_source = raw_display_source
1608
1609        lorehash: typing.Optional[int] = None
1610        if raw_lore_hash := payload.get("loreHash"):
1611            lorehash = int(raw_lore_hash)
1612
1613        summary_hash: typing.Optional[int] = None
1614        if raw_summary_hash := payload.get("summaryItemHash"):
1615            summary_hash = raw_summary_hash
1616
1617        breaker_type_hash: typing.Optional[int] = None
1618        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1619            breaker_type_hash = int(raw_breaker_type_hash)
1620
1621        damage_types: typing.Optional[collections.Sequence[int]] = None
1622        if raw_damage_types := payload.get("damageTypes"):
1623            damage_types = [int(type_) for type_ in raw_damage_types]
1624
1625        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1626        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1627            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1628
1629        default_damagetype_hash: typing.Optional[int] = None
1630        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1631            default_damagetype_hash = int(raw_defaultdmg_hash)
1632
1633        emblem_objective_hash: typing.Optional[int] = None
1634        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1635            emblem_objective_hash = int(raw_emblem_obj_hash)
1636
1637        tier_type: typing.Optional[enums.TierType] = None
1638        tier: typing.Optional[enums.ItemTier] = None
1639        bucket_hash: typing.Optional[int] = None
1640        recovery_hash: typing.Optional[int] = None
1641        tier_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1642        isinstance_item: bool = False
1643        expire_tool_tip: undefined.UndefinedOr[str] = undefined.UNDEFINED
1644        expire_in_orbit_message: undefined.UndefinedOr[str] = undefined.UNDEFINED
1645        suppress_expiration: bool = False
1646        max_stack_size: typing.Optional[int] = None
1647        stack_label: undefined.UndefinedOr[str] = undefined.UNDEFINED
1648
1649        if inventory := payload.get("inventory"):
1650            tier_type = enums.TierType(int(inventory["tierType"]))
1651            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1652            bucket_hash = int(inventory["bucketTypeHash"])
1653            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1654            tier_name = inventory["tierTypeName"]
1655            isinstance_item = inventory["isInstanceItem"]
1656            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1657            max_stack_size = int(inventory["maxStackSize"])
1658
1659            try:
1660                stack_label = inventory["stackUniqueLabel"]
1661            except KeyError:
1662                pass
1663
1664        return entity.InventoryEntity(
1665            net=self._net,
1666            collectible_hash=collectible_hash,
1667            name=props.name,
1668            about=about,
1669            emblem_objective_hash=emblem_objective_hash,
1670            suppress_expiration=suppress_expiration,
1671            max_stack_size=max_stack_size,
1672            stack_label=stack_label,
1673            tier=tier,
1674            tier_type=tier_type,
1675            tier_name=tier_name,
1676            bucket_hash=bucket_hash,
1677            recovery_bucket_hash=recovery_hash,
1678            isinstance_item=isinstance_item,
1679            expire_in_orbit_message=expire_in_orbit_message,
1680            expiration_tooltip=expire_tool_tip,
1681            lore_hash=lorehash,
1682            type_and_tier_name=tier_and_name,
1683            summary_hash=summary_hash,
1684            ui_display_style=ui_item_style,
1685            type_name=type_name,
1686            breaker_type_hash=breaker_type_hash,
1687            description=props.description,
1688            display_source=display_source,
1689            hash=props.hash,
1690            damage_types=damage_types,
1691            index=props.index,
1692            icon=props.icon,
1693            has_icon=props.has_icon,
1694            screenshot=screenshot,
1695            watermark_icon=watermark_icon,
1696            watermark_shelved=watermark_shelved,
1697            secondary_icon=secondary_icon,
1698            secondary_overlay=secondary_overlay,
1699            secondary_special=secondary_special,
1700            type=enums.ItemType(int(payload["itemType"])),
1701            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1702            trait_ids=[trait for trait in payload.get("traitIds", [])],
1703            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1704            item_class=enums.Class(int(payload["classType"])),
1705            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1706            breaker_type=int(payload["breakerType"]),
1707            default_damagetype=int(payload["defaultDamageType"]),
1708            default_damagetype_hash=default_damagetype_hash,
1709            damagetype_hashes=damagetype_hashes,
1710            tooltip_notifications=payload["tooltipNotifications"],
1711            not_transferable=payload["nonTransferrable"],
1712            allow_actions=payload["allowActions"],
1713            is_equippable=payload["equippable"],
1714            objects=objects,
1715            background_colors=payload.get("backgroundColor", {}),
1716            season_hash=payload.get("seasonHash"),
1717            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1718        )
1719
1720    def deserialize_objective_entity(
1721        self, payload: typedefs.JSONObject, /
1722    ) -> entity.ObjectiveEntity:
1723        props = self._set_entity_attrs(payload)
1724        return entity.ObjectiveEntity(
1725            net=self._net,
1726            hash=props.hash,
1727            index=props.index,
1728            description=props.description,
1729            name=props.name,
1730            has_icon=props.has_icon,
1731            icon=props.icon,
1732            unlock_value_hash=payload["unlockValueHash"],
1733            completion_value=payload["completionValue"],
1734            scope=entity.GatingScope(int(payload["scope"])),
1735            location_hash=payload["locationHash"],
1736            allowed_negative_value=payload["allowNegativeValue"],
1737            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1738            counting_downward=payload["isCountingDownward"],
1739            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1740            progress_description=payload["progressDescription"],
1741            perks=payload["perks"],
1742            stats=payload["stats"],
1743            minimum_visibility=payload["minimumVisibilityThreshold"],
1744            allow_over_completion=payload["allowOvercompletion"],
1745            show_value_style=payload["showValueOnComplete"],
1746            display_only_objective=payload["isDisplayOnlyObjective"],
1747            complete_value_style=entity.ValueUIStyle(
1748                int(payload["completedValueStyle"])
1749            ),
1750            progress_value_style=entity.ValueUIStyle(
1751                int(payload["inProgressValueStyle"])
1752            ),
1753            ui_label=payload["uiLabel"],
1754            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1755        )
1756
1757    def _deserialize_activity_values(
1758        self, payload: typedefs.JSONObject, /
1759    ) -> activity.ActivityValues:
1760        team: typing.Optional[int] = None
1761        if raw_team := payload.get("team"):
1762            team = raw_team["basic"]["value"]
1763        return activity.ActivityValues(
1764            assists=payload["assists"]["basic"]["value"],
1765            deaths=payload["deaths"]["basic"]["value"],
1766            kills=payload["kills"]["basic"]["value"],
1767            is_completed=bool(payload["completed"]["basic"]["value"]),
1768            opponents_defeated=payload["opponentsDefeated"]["basic"]["value"],
1769            efficiency=payload["efficiency"]["basic"]["value"],
1770            kd_ratio=payload["killsDeathsRatio"]["basic"]["value"],
1771            kd_assists=payload["killsDeathsAssists"]["basic"]["value"],
1772            score=payload["score"]["basic"]["value"],
1773            duration=payload["activityDurationSeconds"]["basic"]["displayValue"],
1774            team=team,
1775            completion_reason=payload["completionReason"]["basic"]["displayValue"],
1776            fireteam_id=payload["fireteamId"]["basic"]["value"],
1777            start_seconds=payload["startSeconds"]["basic"]["value"],
1778            played_time=payload["timePlayedSeconds"]["basic"]["displayValue"],
1779            player_count=payload["playerCount"]["basic"]["value"],
1780            team_score=payload["teamScore"]["basic"]["value"],
1781        )
1782
1783    def deserialize_activity(
1784        self,
1785        payload: typedefs.JSONObject,
1786        /,
1787    ) -> activity.Activity:
1788        period = time.clean_date(payload["period"])
1789        details = payload["activityDetails"]
1790        ref_id = int(details["referenceId"])
1791        instance_id = int(details["instanceId"])
1792        mode = enums.GameMode(details["mode"])
1793        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1794        is_private = details["isPrivate"]
1795        membership_type = enums.MembershipType(int(details["membershipType"]))
1796
1797        # Since we're using the same fields for post activity method
1798        # this check is required since post activity doesn't values values
1799        values = self._deserialize_activity_values(payload["values"])
1800
1801        return activity.Activity(
1802            net=self._net,
1803            hash=ref_id,
1804            instance_id=instance_id,
1805            mode=mode,
1806            modes=modes,
1807            is_private=is_private,
1808            membership_type=membership_type,
1809            occurred_at=period,
1810            values=values,
1811        )
1812
1813    def deserialize_activities(
1814        self, payload: typedefs.JSONObject
1815    ) -> iterators.Iterator[activity.Activity]:
1816        return iterators.Iterator(
1817            [
1818                self.deserialize_activity(activity_)
1819                for activity_ in payload["activities"]
1820            ]
1821        )
1822
1823    def deserialize_extended_weapon_values(
1824        self, payload: typedefs.JSONObject
1825    ) -> activity.ExtendedWeaponValues:
1826
1827        assists: typing.Optional[int] = None
1828        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1829            assists = raw_assists["basic"]["value"]
1830        assists_damage: typing.Optional[int] = None
1831
1832        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1833            assists_damage = raw_assists_damage["basic"]["value"]
1834
1835        return activity.ExtendedWeaponValues(
1836            reference_id=int(payload["referenceId"]),
1837            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1838            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1839                "value"
1840            ],
1841            assists=assists,
1842            assists_damage=assists_damage,
1843            precision_kills_percentage=(
1844                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1845                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1846                    "displayValue"
1847                ],
1848            ),
1849        )
1850
1851    def _deserialize_extended_values(
1852        self, payload: typedefs.JSONObject
1853    ) -> activity.ExtendedValues:
1854        weapons: typing.Optional[
1855            collections.Collection[activity.ExtendedWeaponValues]
1856        ] = None
1857
1858        if raw_weapons := payload.get("weapons"):
1859            weapons = [
1860                self.deserialize_extended_weapon_values(value) for value in raw_weapons
1861            ]
1862
1863        return activity.ExtendedValues(
1864            precision_kills=payload["values"]["precisionKills"]["basic"]["value"],
1865            grenade_kills=payload["values"]["weaponKillsGrenade"]["basic"]["value"],
1866            melee_kills=payload["values"]["weaponKillsMelee"]["basic"]["value"],
1867            super_kills=payload["values"]["weaponKillsSuper"]["basic"]["value"],
1868            ability_kills=payload["values"]["weaponKillsAbility"]["basic"]["value"],
1869            weapons=weapons,
1870        )
1871
1872    def deserialize_post_activity_player(
1873        self, payload: typedefs.JSONObject, /
1874    ) -> activity.PostActivityPlayer:
1875        player = payload["player"]
1876
1877        class_hash: typedefs.NoneOr[int] = None
1878        if (class_hash := player.get("classHash")) is not None:
1879            class_hash = class_hash
1880
1881        race_hash: typedefs.NoneOr[int] = None
1882        if (race_hash := player.get("raceHash")) is not None:
1883            race_hash = race_hash
1884
1885        gender_hash: typedefs.NoneOr[int] = None
1886        if (gender_hash := player.get("genderHash")) is not None:
1887            gender_hash = gender_hash
1888
1889        character_class: undefined.UndefinedOr[str] = undefined.UNDEFINED
1890        if (
1891            character_class := player.get("characterClass")
1892        ) and not typedefs.is_unknown(character_class):
1893            character_class = character_class
1894
1895        character_level: typedefs.NoneOr[int] = None
1896        if (character_level := player.get("characterLevel")) is not None:
1897            character_level = character_level
1898
1899        return activity.PostActivityPlayer(
1900            standing=int(payload["standing"]),
1901            score=int(payload["score"]["basic"]["value"]),
1902            character_id=payload["characterId"],
1903            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1904            character_class=character_class,
1905            character_level=character_level,
1906            race_hash=race_hash,
1907            gender_hash=gender_hash,
1908            class_hash=class_hash,
1909            light_level=int(player["lightLevel"]),
1910            emblem_hash=int(player["emblemHash"]),
1911            values=self._deserialize_activity_values(payload["values"]),
1912            extended_values=self._deserialize_extended_values(payload["extended"]),
1913        )
1914
1915    def _deserialize_post_activity_team(
1916        self, payload: typedefs.JSONObject
1917    ) -> activity.PostActivityTeam:
1918        return activity.PostActivityTeam(
1919            id=payload["teamId"],
1920            is_defeated=bool(payload["standing"]["basic"]["value"]),
1921            score=int(payload["score"]["basic"]["value"]),
1922            name=payload["teamName"],
1923        )
1924
1925    def deserialize_post_activity(
1926        self, payload: typedefs.JSONObject
1927    ) -> activity.PostActivity:
1928        period = time.clean_date(payload["period"])
1929        details = payload["activityDetails"]
1930        ref_id = int(details["referenceId"])
1931        instance_id = int(details["instanceId"])
1932        mode = enums.GameMode(details["mode"])
1933        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1934        is_private = details["isPrivate"]
1935        membership_type = enums.MembershipType(int(details["membershipType"]))
1936        return activity.PostActivity(
1937            net=self._net,
1938            hash=ref_id,
1939            membership_type=membership_type,
1940            instance_id=instance_id,
1941            mode=mode,
1942            modes=modes,
1943            is_private=is_private,
1944            occurred_at=period,
1945            starting_phase=int(payload["startingPhaseIndex"]),
1946            players=[
1947                self.deserialize_post_activity_player(player)
1948                for player in payload["entries"]
1949            ],
1950            teams=[
1951                self._deserialize_post_activity_team(team) for team in payload["teams"]
1952            ],
1953        )
1954
1955    def _deserialize_aggregated_activity_values(
1956        self, payload: typedefs.JSONObject
1957    ) -> activity.AggregatedActivityValues:
1958        # This ID is always the same for all aggregated values.
1959        activity_id = int(payload["fastestCompletionMsForActivity"]["activityId"])
1960
1961        return activity.AggregatedActivityValues(
1962            id=activity_id,
1963            fastest_completion_time=(
1964                int(payload["fastestCompletionMsForActivity"]["basic"]["value"]),
1965                payload["fastestCompletionMsForActivity"]["basic"]["displayValue"],
1966            ),
1967            completions=int(payload["activityCompletions"]["basic"]["value"]),
1968            kills=int(payload["activityKills"]["basic"]["value"]),
1969            deaths=int(payload["activityDeaths"]["basic"]["value"]),
1970            assists=int(payload["activityAssists"]["basic"]["value"]),
1971            seconds_played=(
1972                int(payload["activitySecondsPlayed"]["basic"]["value"]),
1973                payload["activitySecondsPlayed"]["basic"]["displayValue"],
1974            ),
1975            wins=int(payload["activityWins"]["basic"]["value"]),
1976            goals_missed=int(payload["activityGoalsMissed"]["basic"]["value"]),
1977            special_actions=int(payload["activitySpecialActions"]["basic"]["value"]),
1978            best_goals_hit=int(payload["activityBestGoalsHit"]["basic"]["value"]),
1979            best_single_score=int(
1980                payload["activityBestSingleGameScore"]["basic"]["value"]
1981            ),
1982            goals_hit=int(payload["activityGoalsHit"]["basic"]["value"]),
1983            special_score=int(payload["activitySpecialScore"]["basic"]["value"]),
1984            kd_assists=int(payload["activityKillsDeathsAssists"]["basic"]["value"]),
1985            kd_ratio=float(
1986                payload["activityKillsDeathsAssists"]["basic"]["displayValue"]
1987            ),
1988            precision_kills=int(payload["activityPrecisionKills"]["basic"]["value"]),
1989        )
1990
1991    def deserialize_aggregated_activity(
1992        self, payload: typedefs.JSONObject
1993    ) -> activity.AggregatedActivity:
1994        return activity.AggregatedActivity(
1995            hash=int(payload["activityHash"]),
1996            values=self._deserialize_aggregated_activity_values(payload["values"]),
1997        )
1998
1999    def deserialize_aggregated_activities(
2000        self, payload: typedefs.JSONObject
2001    ) -> iterators.Iterator[activity.AggregatedActivity]:
2002        return iterators.Iterator(
2003            [
2004                self.deserialize_aggregated_activity(activity)
2005                for activity in payload["activities"]
2006            ]
2007        )
2008
2009    def deserialize_linked_profiles(
2010        self, payload: typedefs.JSONObject
2011    ) -> profile.LinkedProfile:
2012        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
2013        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2014        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2015
2016        if raw_profile := payload.get("profiles"):
2017            for pfile in raw_profile:
2018                profiles_vec.append(self.deserialize_destiny_membership(pfile))
2019
2020        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
2021            for raw_error_pfile in raw_profiles_with_errors:
2022                if error_pfile := raw_error_pfile.get("infoCard"):
2023                    error_profiles_vec.append(
2024                        self.deserialize_destiny_membership(error_pfile)
2025                    )
2026
2027        return profile.LinkedProfile(
2028            net=self._net,
2029            bungie=bungie_user,
2030            profiles=profiles_vec,
2031            profiles_with_errors=error_profiles_vec,
2032        )
2033
2034    def deserialize_clan_banners(
2035        self, payload: typedefs.JSONObject
2036    ) -> collections.Sequence[clans.ClanBanner]:
2037        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
2038        if banners := payload.get("clanBannerDecals"):
2039            for k, v in banners.items():
2040                banner_obj = clans.ClanBanner(
2041                    id=int(k),
2042                    foreground=assets.Image(v["foregroundPath"]),
2043                    background=assets.Image(v["backgroundPath"]),
2044                )
2045                banners_seq.append(banner_obj)
2046        return banners_seq
2047
2048    def deserialize_public_milestone_content(
2049        self, payload: typedefs.JSONObject
2050    ) -> milestones.MilestoneContent:
2051        items_categoris: typedefs.NoneOr[milestones.MilestoneItems] = None
2052        if raw_categories := payload.get("itemCategories"):
2053            for item in raw_categories:
2054                title = undefined.UNDEFINED
2055                if raw_title := item.get("title"):
2056                    if raw_title != typedefs.Unknown:
2057                        title = raw_title
2058                if raw_hashes := item.get("itemHashes"):
2059                    hashes: collections.Sequence[int] = raw_hashes
2060
2061                items_categoris = milestones.MilestoneItems(title=title, hashes=hashes)
2062
2063        about = undefined.UNDEFINED
2064        if (raw_about := payload["about"]) != typedefs.Unknown:
2065            about = raw_about
2066
2067        status = undefined.UNDEFINED
2068        if (raw_status := payload["status"]) != typedefs.Unknown:
2069            status = raw_status
2070
2071        tips: typing.MutableSequence[undefined.UndefinedOr[str]] = []
2072        if raw_tips := payload.get("tips"):
2073            for raw_tip in raw_tips:
2074                if raw_tip == typedefs.Unknown:
2075                    raw_tip = undefined.UNDEFINED
2076                tips.append(raw_tip)
2077
2078        return milestones.MilestoneContent(
2079            about=about, status=status, tips=tips, items=items_categoris
2080        )
2081
2082    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2083        name = undefined.UNDEFINED
2084        if (raw_name := payload["bungieGlobalDisplayName"]) != typedefs.Unknown:
2085            name = raw_name
2086
2087        bungie_user: typedefs.NoneOr[user.BungieUser] = None
2088
2089        if raw_bungie_user := payload.get("bungieNetUser"):
2090            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2091
2092        return friends.Friend(
2093            net=self._net,
2094            id=int(payload["lastSeenAsMembershipId"]),
2095            name=name,
2096            code=payload.get("bungieGlobalDisplayNameCode"),
2097            relationship=enums.Relationship(payload["relationship"]),
2098            user=bungie_user,
2099            online_status=enums.Presence(payload["onlineStatus"]),
2100            online_title=payload["onlineTitle"],
2101            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2102        )
2103
2104    def deserialize_friends(
2105        self, payload: typedefs.JSONObject
2106    ) -> collections.Sequence[friends.Friend]:
2107        mut_seq: typing.MutableSequence[friends.Friend] = []
2108        if raw_friends := payload.get("friends"):
2109            for friend in raw_friends:
2110                mut_seq.append(self.deserialize_friend(friend))
2111        return mut_seq
2112
2113    def deserialize_friend_requests(
2114        self, payload: typedefs.JSONObject
2115    ) -> friends.FriendRequestView:
2116        incoming: typing.MutableSequence[friends.Friend] = []
2117        outgoing: typing.MutableSequence[friends.Friend] = []
2118
2119        if raw_incoming_requests := payload.get("incomingRequests"):
2120            for incoming_request in raw_incoming_requests:
2121                incoming.append(self.deserialize_friend(incoming_request))
2122
2123        if raw_outgoing_requests := payload.get("outgoingRequests"):
2124            for outgoing_request in raw_outgoing_requests:
2125                outgoing.append(self.deserialize_friend(outgoing_request))
2126
2127        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)
2128
2129    def _set_fireteam_fields(
2130        self, payload: typedefs.JSONObject, total_results: typing.Optional[int] = None
2131    ) -> fireteams.Fireteam:
2132        activity_type = fireteams.FireteamActivity(payload["activityType"])
2133        return fireteams.Fireteam(
2134            id=int(payload["fireteamId"]),
2135            group_id=int(payload["groupId"]),
2136            platform=fireteams.FireteamPlatform(payload["platform"]),
2137            is_immediate=payload["isImmediate"],
2138            activity_type=activity_type,
2139            owner_id=int(payload["ownerMembershipId"]),
2140            player_slot_count=payload["playerSlotCount"],
2141            available_player_slots=payload["availablePlayerSlotCount"],
2142            available_alternate_slots=payload["availableAlternateSlotCount"],
2143            title=payload["title"],
2144            date_created=time.clean_date(payload["dateCreated"]),
2145            is_public=payload["isPublic"],
2146            locale=fireteams.FireteamLanguage(payload["locale"]),
2147            is_valid=payload["isValid"],
2148            last_modified=time.clean_date(payload["datePlayerModified"]),
2149            total_results=total_results or 0,
2150        )
2151
2152    def deserialize_fireteams(
2153        self, payload: typedefs.JSONObject
2154    ) -> typedefs.NoneOr[collections.Sequence[fireteams.Fireteam]]:
2155        fireteams_: typing.MutableSequence[fireteams.Fireteam] = []
2156
2157        result: list[typedefs.JSONObject]
2158        if not (result := payload["results"]):
2159            return None
2160        for elem in result:
2161            fireteams_.append(
2162                self._set_fireteam_fields(
2163                    elem, total_results=int(payload["totalResults"])
2164                )
2165            )
2166        return fireteams_
2167
2168    def deserialize_fireteam_destiny_users(
2169        self, payload: typedefs.JSONObject
2170    ) -> fireteams.FireteamUser:
2171        destiny_obj = self.deserialize_destiny_membership(payload)
2172        # We could helpers.just return a DestinyMembership object but this is
2173        # missing the fireteam display name and id fields.
2174        return fireteams.FireteamUser(
2175            net=self._net,
2176            id=destiny_obj.id,
2177            code=destiny_obj.code,
2178            icon=destiny_obj.icon,
2179            types=destiny_obj.types,
2180            type=destiny_obj.type,
2181            is_public=destiny_obj.is_public,
2182            crossave_override=destiny_obj.crossave_override,
2183            name=destiny_obj.name,
2184            last_seen_name=destiny_obj.last_seen_name,
2185            fireteam_display_name=payload["FireteamDisplayName"],
2186            fireteam_membership_id=enums.MembershipType(
2187                payload["FireteamMembershipType"]
2188            ),
2189        )
2190
2191    def deserialize_fireteam_members(
2192        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2193    ) -> typing.Optional[collections.Sequence[fireteams.FireteamMember]]:
2194        members_: list[fireteams.FireteamMember] = []
2195        if members := payload.get("Members" if not alternatives else "Alternates"):
2196            for member in members:
2197                bungie_fields = self.deserialize_partial_bungie_user(member)
2198                members_fields = fireteams.FireteamMember(
2199                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2200                    has_microphone=member["hasMicrophone"],
2201                    character_id=int(member["characterId"]),
2202                    date_joined=time.clean_date(member["dateJoined"]),
2203                    last_platform_invite_date=time.clean_date(
2204                        member["lastPlatformInviteAttemptDate"]
2205                    ),
2206                    last_platform_invite_result=int(
2207                        member["lastPlatformInviteAttemptResult"]
2208                    ),
2209                    net=self._net,
2210                    name=bungie_fields.name,
2211                    id=bungie_fields.id,
2212                    icon=bungie_fields.icon,
2213                    is_public=bungie_fields.is_public,
2214                    crossave_override=bungie_fields.crossave_override,
2215                    types=bungie_fields.types,
2216                    type=bungie_fields.type,
2217                )
2218                members_.append(members_fields)
2219        else:
2220            return None
2221        return members_
2222
2223    def deserialize_available_fireteams(
2224        self,
2225        data: typedefs.JSONObject,
2226        *,
2227        no_results: bool = False,
2228    ) -> typing.Union[
2229        fireteams.AvailableFireteam, collections.Sequence[fireteams.AvailableFireteam]
2230    ]:
2231        fireteams_: list[fireteams.AvailableFireteam] = []
2232
2233        # This needs to be used outside the results
2234        # JSON key.
2235        if no_results is True:
2236            payload = data
2237
2238        if result := payload.get("results"):
2239
2240            for fireteam in result:
2241                found_fireteams = self._set_fireteam_fields(fireteam["Summary"])
2242                fireteams_fields = fireteams.AvailableFireteam(
2243                    id=found_fireteams.id,
2244                    group_id=found_fireteams.group_id,
2245                    platform=found_fireteams.platform,
2246                    activity_type=found_fireteams.activity_type,
2247                    is_immediate=found_fireteams.is_immediate,
2248                    is_public=found_fireteams.is_public,
2249                    is_valid=found_fireteams.is_valid,
2250                    owner_id=found_fireteams.owner_id,
2251                    player_slot_count=found_fireteams.player_slot_count,
2252                    available_player_slots=found_fireteams.available_player_slots,
2253                    available_alternate_slots=found_fireteams.available_alternate_slots,
2254                    title=found_fireteams.title,
2255                    date_created=found_fireteams.date_created,
2256                    locale=found_fireteams.locale,
2257                    last_modified=found_fireteams.last_modified,
2258                    total_results=found_fireteams.total_results,
2259                    members=self.deserialize_fireteam_members(payload),
2260                    alternatives=self.deserialize_fireteam_members(
2261                        payload, alternatives=True
2262                    ),
2263                )
2264            fireteams_.append(fireteams_fields)
2265            if no_results:
2266                return fireteams_fields
2267        return fireteams_
2268
2269    def deserialize_fireteam_party(
2270        self, payload: typedefs.JSONObject
2271    ) -> fireteams.FireteamParty:
2272        last_destination_hash: typing.Optional[int] = None
2273        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2274            last_destination_hash = int(raw_dest_hash)
2275
2276        return fireteams.FireteamParty(
2277            members=[
2278                self._deserialize_fireteam_party_member(member)
2279                for member in payload["partyMembers"]
2280            ],
2281            activity=self._deserialize_fireteam_party_current_activity(
2282                payload["currentActivity"]
2283            ),
2284            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2285            last_destination_hash=last_destination_hash,
2286            tracking=payload["tracking"],
2287        )
2288
2289    def _deserialize_fireteam_party_member(
2290        self, payload: typedefs.JSONObject
2291    ) -> fireteams.FireteamPartyMember:
2292
2293        status = fireteams.FireteamPartyMemberState(payload["status"])
2294        displayname: undefined.UndefinedOr[str] = undefined.UNDEFINED
2295        if raw_name := payload.get("displayName"):
2296            displayname = raw_name
2297
2298        return fireteams.FireteamPartyMember(
2299            membership_id=int(payload["membershipId"]),
2300            emblem_hash=int(payload["emblemHash"]),
2301            status=status,
2302            display_name=displayname,
2303        )
2304
2305    def _deserialize_fireteam_party_current_activity(
2306        self, payload: typedefs.JSONObject
2307    ) -> fireteams.FireteamPartyCurrentActivity:
2308        start_date: typing.Optional[datetime.datetime] = None
2309        if raw_start_date := payload.get("startTime"):
2310            start_date = time.clean_date(raw_start_date)
2311
2312        end_date: typing.Optional[datetime.datetime] = None
2313        if raw_end_date := payload.get("endTime"):
2314            end_date = time.clean_date(raw_end_date)
2315        return fireteams.FireteamPartyCurrentActivity(
2316            start_time=start_date,
2317            end_time=end_date,
2318            score=float(payload["score"]),
2319            highest_opposing_score=float(payload["highestOpposingFactionScore"]),
2320            opponenst_count=int(payload["numberOfOpponents"]),
2321            player_count=int(payload["numberOfPlayers"]),
2322        )
2323
2324    def _deserialize_fireteam_party_settings(
2325        self, payload: typedefs.JSONObject
2326    ) -> fireteams.FireteamPartySettings:
2327        closed_reasons = enums.ClosedReasons(payload["closedReasons"])
2328        return fireteams.FireteamPartySettings(
2329            open_slots=int(payload["openSlots"]),
2330            privacy_setting=enums.PrivacySetting(int(payload["privacySetting"])),
2331            closed_reasons=closed_reasons,
2332        )
2333
2334    def deserialize_seasonal_artifact(
2335        self, payload: typedefs.JSONObject
2336    ) -> season.Artifact:
2337        if raw_artifact := payload.get("seasonalArtifact"):
2338            if points := raw_artifact.get("pointProgression"):
2339                points_prog = progressions.Progression(
2340                    hash=points["progressionHash"],
2341                    level=points["level"],
2342                    cap=points["levelCap"],
2343                    daily_limit=points["dailyLimit"],
2344                    weekly_limit=points["weeklyLimit"],
2345                    current_progress=points["currentProgress"],
2346                    daily_progress=points["dailyProgress"],
2347                    needed=points["progressToNextLevel"],
2348                    next_level=points["nextLevelAt"],
2349                )
2350
2351            if bonus := raw_artifact.get("powerBonusProgression"):
2352                power_bonus_prog = progressions.Progression(
2353                    hash=bonus["progressionHash"],
2354                    level=bonus["level"],
2355                    cap=bonus["levelCap"],
2356                    daily_limit=bonus["dailyLimit"],
2357                    weekly_limit=bonus["weeklyLimit"],
2358                    current_progress=bonus["currentProgress"],
2359                    daily_progress=bonus["dailyProgress"],
2360                    needed=bonus["progressToNextLevel"],
2361                    next_level=bonus["nextLevelAt"],
2362                )
2363            artifact = season.Artifact(
2364                net=self._net,
2365                hash=raw_artifact["artifactHash"],
2366                power_bonus=raw_artifact["powerBonus"],
2367                acquired_points=raw_artifact["pointsAcquired"],
2368                bonus=power_bonus_prog,
2369                points=points_prog,
2370            )
2371        return artifact
2372
2373    def deserialize_profile_progression(
2374        self, payload: typedefs.JSONObject
2375    ) -> profile.ProfileProgression:
2376        return profile.ProfileProgression(
2377            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2378            checklist={
2379                int(check_id): checklists
2380                for check_id, checklists in payload["data"]["checklists"].items()
2381            },
2382        )
2383
2384    def deserialize_instanced_item(
2385        self, payload: typedefs.JSONObject
2386    ) -> items.ItemInstance:
2387        damage_type_hash: typing.Optional[int] = None
2388        if raw_damagetype_hash := payload.get("damageTypeHash"):
2389            damage_type_hash = int(raw_damagetype_hash)
2390
2391        required_hashes: typing.Optional[collections.Collection[int]] = None
2392        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2393            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2394
2395        breaker_type: typing.Optional[items.ItemBreakerType] = None
2396        if raw_break_type := payload.get("breakerType"):
2397            breaker_type = items.ItemBreakerType(int(raw_break_type))
2398
2399        breaker_type_hash: typing.Optional[int] = None
2400        if raw_break_type_hash := payload.get("breakerTypeHash"):
2401            breaker_type_hash = int(raw_break_type_hash)
2402
2403        energy: typing.Optional[items.ItemEnergy] = None
2404        if raw_energy := payload.get("energy"):
2405            energy = self.deserialize_item_energy(raw_energy)
2406
2407        primary_stats = None
2408        if raw_primary_stats := payload.get("primaryStat"):
2409            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2410
2411        return items.ItemInstance(
2412            damage_type=enums.DamageType(int(payload["damageType"])),
2413            damage_type_hash=damage_type_hash,
2414            primary_stat=primary_stats,
2415            item_level=int(payload["itemLevel"]),
2416            quality=int(payload["quality"]),
2417            is_equipped=payload["isEquipped"],
2418            can_equip=payload["canEquip"],
2419            equip_required_level=int(payload["equipRequiredLevel"]),
2420            required_equip_unlock_hashes=required_hashes,
2421            cant_equip_reason=int(payload["cannotEquipReason"]),
2422            breaker_type=breaker_type,
2423            breaker_type_hash=breaker_type_hash,
2424            energy=energy,
2425        )
2426
2427    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2428        energy_hash: typing.Optional[int] = None
2429        if raw_energy_hash := payload.get("energyTypeHash"):
2430            energy_hash = int(raw_energy_hash)
2431
2432        return items.ItemEnergy(
2433            hash=energy_hash,
2434            type=items.ItemEnergyType(int(payload["energyType"])),
2435            capacity=int(payload["energyCapacity"]),
2436            used_energy=int(payload["energyUsed"]),
2437            unused_energy=int(payload["energyUnused"]),
2438        )
2439
2440    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2441        perk_hash: typing.Optional[int] = None
2442        if raw_perk_hash := payload.get("perkHash"):
2443            perk_hash = int(raw_perk_hash)
2444
2445        return items.ItemPerk(
2446            hash=perk_hash,
2447            icon=assets.Image(payload["iconPath"]),
2448            is_active=payload["isActive"],
2449            is_visible=payload["visible"],
2450        )
2451
2452    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2453        plug_hash: typing.Optional[int] = None
2454        if raw_plug_hash := payload.get("plugHash"):
2455            plug_hash = int(raw_plug_hash)
2456
2457        enable_fail_indexes: typing.Optional[list[int]] = None
2458        if raw_indexes := payload.get("enableFailIndexes"):
2459            enable_fail_indexes = [int(index) for index in raw_indexes]
2460
2461        return items.ItemSocket(
2462            plug_hash=plug_hash,
2463            is_enabled=payload["isEnabled"],
2464            enable_fail_indexes=enable_fail_indexes,
2465            is_visible=payload.get("visible"),
2466        )
2467
2468    def deserialize_item_stats_view(
2469        self, payload: typedefs.JSONObject
2470    ) -> items.ItemStatsView:
2471        return items.ItemStatsView(
2472            stat_hash=payload.get("statHash"), value=payload.get("value")
2473        )
2474
2475    def deserialize_plug_item_state(
2476        self, payload: typedefs.JSONObject
2477    ) -> items.PlugItemState:
2478        item_hash: typing.Optional[int] = None
2479        if raw_item_hash := payload.get("plugItemHash"):
2480            item_hash = int(raw_item_hash)
2481
2482        insert_fail_indexes: typedefs.NoneOr[list[int]] = None
2483        if raw_fail_indexes := payload.get("insertFailIndexes"):
2484            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2485
2486        enable_fail_indexes: typedefs.NoneOr[list[int]] = None
2487        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2488            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2489
2490        return items.PlugItemState(
2491            item_hash=item_hash,
2492            insert_fail_indexes=insert_fail_indexes,
2493            enable_fail_indexes=enable_fail_indexes,
2494            is_enabled=payload["enabled"],
2495            can_insert=payload["canInsert"],
2496        )

The base deserialization factory class for all aiobungie objects.

Highly inspired hikari entity factory used to deserialize JSON responses from the REST client and turning them into a aiobungie.crates Python classes.

Factory(net: aiobungie.traits.Netrunner)
70    def __init__(self, net: traits.Netrunner) -> None:
71        self._net = net
def deserialize_bungie_user(self, data: dict[str, typing.Any]) -> aiobungie.crates.user.BungieUser:
73    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
74        return user.BungieUser(
75            id=int(data["membershipId"]),
76            created_at=time.clean_date(data["firstAccess"]),
77            name=data.get("cachedBungieGlobalDisplayName", undefined.UNDEFINED),
78            is_deleted=data["isDeleted"],
79            about=data["about"],
80            updated_at=time.clean_date(data["lastUpdate"]),
81            psn_name=data.get("psnDisplayName", None),
82            stadia_name=data.get("stadiaDisplayName", None),
83            steam_name=data.get("steamDisplayName", None),
84            twitch_name=data.get("twitchDisplayName", None),
85            blizzard_name=data.get("blizzardDisplayName", None),
86            status=data["statusText"],
87            locale=data["locale"],
88            picture=assets.Image(path=str(data["profilePicturePath"])),
89            code=data.get("cachedBungieGlobalDisplayNameCode", None),
90            unique_name=data.get("uniqueName", None),
91            theme_id=int(data["profileTheme"]),
92            show_activity=bool(data["showActivity"]),
93            theme_name=data["profileThemeName"],
94            display_title=data["userTitleDisplay"],
95        )

Deserialize a raw JSON Bungie.net user only payload into a user object.

This only returns the Bungie.net user and not the Destiny memberships.

Parameters
Returns
def deserialize_partial_bungie_user( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.PartialBungieUser:
 97    def deserialize_partial_bungie_user(
 98        self, payload: typedefs.JSONObject
 99    ) -> user.PartialBungieUser:
100        return user.PartialBungieUser(
101            net=self._net,
102            types=[
103                enums.MembershipType(type_)
104                for type_ in payload.get("applicableMembershipTypes", [])
105            ],
106            name=payload.get("displayName", undefined.UNDEFINED),
107            id=int(payload["membershipId"]),
108            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
109            is_public=payload["isPublic"],
110            icon=assets.Image(payload.get("iconPath", "")),
111            type=enums.MembershipType(payload["membershipType"]),
112        )

Deserialize a raw JSON of a partial bungieNetUserInfo.

A partial user is a bungie.net user payload with missing information from the main BungieUser object.

Parameters
Returns
def deserialize_destiny_membership( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.DestinyMembership:
114    def deserialize_destiny_membership(
115        self, payload: typedefs.JSONObject
116    ) -> user.DestinyMembership:
117        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
118        if (
119            raw_name := payload.get("bungieGlobalDisplayName", "")
120        ) and not typedefs.is_unknown(raw_name):
121            name = raw_name
122
123        return user.DestinyMembership(
124            net=self._net,
125            id=int(payload["membershipId"]),
126            name=name,
127            code=payload.get("bungieGlobalDisplayNameCode", None),
128            last_seen_name=payload.get("LastSeenDisplayName")
129            or payload.get("displayName")  # noqa: W503
130            or "",  # noqa: W503
131            type=enums.MembershipType(payload["membershipType"]),
132            is_public=payload["isPublic"],
133            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
134            icon=assets.Image(payload.get("iconPath", "")),
135            types=[
136                enums.MembershipType(type_)
137                for type_ in payload.get("applicableMembershipTypes", [])
138            ],
139        )

Deserialize a raw JSON of destinyUserInfo destiny membership information.

Parameters
Returns
  • aiobungie.crates.user.DestinyMembership: A Destiny 2 membership.
def deserialize_destiny_memberships( self, data: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
141    def deserialize_destiny_memberships(
142        self, data: typedefs.JSONArray
143    ) -> collections.Sequence[user.DestinyMembership]:
144        return [self.deserialize_destiny_membership(membership) for membership in data]

Deserialize a raw JSON payload/array of destinyUserInfo.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.DestinyMembership]: A sequence of Destiny 2 memberships.
def deserialize_user(self, data: dict[str, typing.Any]) -> aiobungie.crates.user.User:
146    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
147
148        primary_membership_id: typing.Optional[int] = None
149        if raw_primary_id := data.get("primaryMembershipId"):
150            primary_membership_id = int(raw_primary_id)
151
152        return user.User(
153            bungie=self.deserialize_bungie_user(data["bungieNetUser"]),
154            destiny=self.deserialize_destiny_memberships(data["destinyMemberships"]),
155            primary_membership_id=primary_membership_id,
156        )

Deserialize a raw JSON results of fetched user memberships and Bungie.net user its their id.

Parameters
Returns
def deserialize_searched_user( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.SearchableDestinyUser:
158    def deserialize_searched_user(
159        self, payload: typedefs.JSONObject
160    ) -> user.SearchableDestinyUser:
161        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
162        if (raw_name := payload["bungieGlobalDisplayName"]) and not typedefs.is_unknown(
163            raw_name
164        ):
165            name = raw_name
166
167        code: typing.Optional[int] = None
168        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
169            code = int(raw_code)
170
171        bungie_id: typing.Optional[int] = None
172        if raw_bungie_id := payload.get("bungieNetMembershipId"):
173            bungie_id = int(raw_bungie_id)
174
175        return user.SearchableDestinyUser(
176            name=name,
177            code=code,
178            bungie_id=bungie_id,
179            memberships=self.deserialize_destiny_memberships(
180                payload["destinyMemberships"]
181            ),
182        )

Deserialize the results of user search details.

Parameters
Returns
def deserialize_user_credentials( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
184    def deserialize_user_credentials(
185        self, payload: typedefs.JSONArray
186    ) -> collections.Sequence[user.UserCredentials]:
187        return [
188            user.UserCredentials(
189                type=enums.CredentialType(int(creds["credentialType"])),
190                display_name=creds["credentialDisplayName"],
191                is_public=creds["isPublic"],
192                self_as_string=creds.get("credentialAsString", undefined.UNDEFINED),
193            )
194            for creds in payload
195        ]

Deserialize a JSON array of Bungie user credentials.

Parameters
Returns
def deserialize_user_themes( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
197    def deserialize_user_themes(
198        self, payload: typedefs.JSONArray
199    ) -> collections.Sequence[user.UserThemes]:
200        return [
201            user.UserThemes(
202                id=int(entry["userThemeId"]),
203                name=entry["userThemeName"]
204                if "userThemeName" in entry
205                else undefined.UNDEFINED,
206                description=entry["userThemeDescription"]
207                if "userThemeDescription" in entry
208                else undefined.UNDEFINED,
209            )
210            for entry in payload
211        ]

Deserialize a raw JSON array of Bungie user themes.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of bungie user themes.
def deserialize_clan(self, payload: dict[str, typing.Any]) -> aiobungie.crates.clans.Clan:
213    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
214
215        # This is kinda redundant
216        data = payload
217
218        # This is always outside the details.
219        current_user_map: typing.Optional[
220            collections.Mapping[str, clans.ClanMember]
221        ] = None
222        if raw_current_user_map := payload.get("currentUserMemberMap"):
223            current_user_map = {
224                membership_type: self.deserialize_clan_member(membership)
225                for membership_type, membership in raw_current_user_map.items()
226            }
227
228        try:
229            data = payload["detail"]
230        except KeyError:
231            pass
232
233        id = data["groupId"]
234        name = data["name"]
235        created_at = data["creationDate"]
236        member_count = data["memberCount"]
237        about = data["about"]
238        motto = data["motto"]
239        is_public = data["isPublic"]
240        banner = assets.Image(str(data["bannerPath"]))
241        avatar = assets.Image(str(data["avatarPath"]))
242        tags = data["tags"]
243        type = data["groupType"]
244
245        features = data["features"]
246        features_obj = clans.ClanFeatures(
247            max_members=features["maximumMembers"],
248            max_membership_types=features["maximumMembershipsOfGroupType"],
249            capabilities=features["capabilities"],
250            membership_types=features["membershipTypes"],
251            invite_permissions=features["invitePermissionOverride"],
252            update_banner_permissions=features["updateBannerPermissionOverride"],
253            update_culture_permissions=features["updateCulturePermissionOverride"],
254            join_level=features["joinLevel"],
255        )
256
257        information: typedefs.JSONObject = data["clanInfo"]
258        progression: collections.Mapping[int, progressions.Progression] = {
259            int(prog_hash): self.deserialize_progressions(prog)
260            for prog_hash, prog in information["d2ClanProgressions"].items()
261        }
262
263        founder: typedefs.NoneOr[clans.ClanMember] = None
264        if raw_founder := payload.get("founder"):
265            founder = self.deserialize_clan_member(raw_founder)
266
267        return clans.Clan(
268            net=self._net,
269            id=int(id),
270            name=name,
271            type=enums.GroupType(type),
272            created_at=time.clean_date(created_at),
273            member_count=member_count,
274            motto=motto,
275            about=about,
276            is_public=is_public,
277            banner=banner,
278            avatar=avatar,
279            tags=tags,
280            features=features_obj,
281            owner=founder,
282            progressions=progression,
283            call_sign=information["clanCallsign"],
284            banner_data=information["clanBannerData"],
285            chat_security=data["chatSecurity"],
286            conversation_id=int(data["conversationId"]),
287            allow_chat=data["allowChat"],
288            theme=data["theme"],
289            current_user_membership=current_user_map,
290        )

Deserialize a raw JSON payload of Bungie clan information.

Parameters
Returns
def deserialize_clan_member( self, data: dict[str, typing.Any], /) -> aiobungie.crates.clans.ClanMember:
292    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
293        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
294        return clans.ClanMember(
295            net=self._net,
296            last_seen_name=destiny_user.last_seen_name,
297            id=destiny_user.id,
298            name=destiny_user.name,
299            icon=destiny_user.icon,
300            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
301            group_id=int(data["groupId"]),
302            joined_at=time.clean_date(data["joinDate"]),
303            types=destiny_user.types,
304            is_public=destiny_user.is_public,
305            type=destiny_user.type,
306            code=destiny_user.code,
307            is_online=data["isOnline"],
308            crossave_override=destiny_user.crossave_override,
309            bungie=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
310            if "bungieNetUserInfo" in data
311            else None,
312            member_type=enums.ClanMemberType(int(data["memberType"])),
313        )

Deserialize a JSON payload of a clan member information.

Parameters
Returns
def deserialize_clan_members( self, data: dict[str, typing.Any], /) -> aiobungie.Iterator[aiobungie.crates.clans.ClanMember]:
315    def deserialize_clan_members(
316        self, data: typedefs.JSONObject, /
317    ) -> iterators.Iterator[clans.ClanMember]:
318        return iterators.Iterator(
319            [self.deserialize_clan_member(member) for member in data["results"]]
320        )

Deserialize a JSON payload of a clan members information.

Parameters
Returns
def deserialize_group_member( self, payload: dict[str, typing.Any]) -> aiobungie.crates.clans.GroupMember:
322    def deserialize_group_member(
323        self, payload: typedefs.JSONObject
324    ) -> clans.GroupMember:
325        member = payload["member"]
326        return clans.GroupMember(
327            net=self._net,
328            join_date=time.clean_date(member["joinDate"]),
329            group_id=int(member["groupId"]),
330            member_type=enums.ClanMemberType(member["memberType"]),
331            is_online=member["isOnline"],
332            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
333            inactive_memberships=payload.get("areAllMembershipsInactive", None),
334            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
335            group=self.deserialize_clan(payload["group"]),
336        )

Deserialize a JSON payload of group information for a member.

Parameters
Returns
def deserialize_clan_conversations( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
354    def deserialize_clan_conversations(
355        self, payload: typedefs.JSONArray
356    ) -> collections.Sequence[clans.ClanConversation]:
357        return [self._deserialize_clan_conversation(conv) for conv in payload]

Deserialize a JSON array of a clan conversations information.

Parameters
Returns
def deserialize_app_owner( self, payload: dict[str, typing.Any]) -> aiobungie.crates.application.ApplicationOwner:
359    def deserialize_app_owner(
360        self, payload: typedefs.JSONObject
361    ) -> application.ApplicationOwner:
362        return application.ApplicationOwner(
363            net=self._net,
364            name=payload.get("bungieGlobalDisplayName", undefined.UNDEFINED),
365            id=int(payload["membershipId"]),
366            type=enums.MembershipType(payload["membershipType"]),
367            icon=assets.Image(str(payload["iconPath"])),
368            is_public=payload["isPublic"],
369            code=payload.get("bungieGlobalDisplayNameCode", None),
370        )

Deserialize a JSON payload of Bungie Developer portal application owner information.

Parameters
Returns
  • aiobungie.crates.application.ApplicationOwner: An application owner.
def deserialize_app( self, payload: dict[str, typing.Any]) -> aiobungie.crates.application.Application:
372    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
373        return application.Application(
374            id=int(payload["applicationId"]),
375            name=payload["name"],
376            link=payload["link"],
377            status=payload["status"],
378            redirect_url=payload.get("redirectUrl", None),
379            created_at=time.clean_date(str(payload["creationDate"])),
380            published_at=time.clean_date(str(payload["firstPublished"])),
381            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
382            scope=payload.get("scope", undefined.UNDEFINED),
383        )

Deserialize a JSON payload of Bungie Developer portal application information.

Parameters
Returns
  • aiobungie.crates.application.Application: An application.
def deserialize_profile( self, payload: dict[str, typing.Any], /) -> Optional[aiobungie.crates.profile.Profile]:
406    def deserialize_profile(
407        self, payload: typedefs.JSONObject, /
408    ) -> typing.Optional[profile.Profile]:
409        if (raw_profile := payload.get("data")) is None:
410            return None
411
412        payload = raw_profile
413        id = int(payload["userInfo"]["membershipId"])
414        name = payload["userInfo"]["displayName"]
415        is_public = payload["userInfo"]["isPublic"]
416        type = enums.MembershipType(payload["userInfo"]["membershipType"])
417        last_played = time.clean_date(str(payload["dateLastPlayed"]))
418        character_ids = [int(cid) for cid in payload["characterIds"]]
419        power_cap = payload["currentSeasonRewardPowerCap"]
420
421        return profile.Profile(
422            id=int(id),
423            name=name,
424            is_public=is_public,
425            type=type,
426            last_played=last_played,
427            character_ids=character_ids,
428            power_cap=power_cap,
429            net=self._net,
430        )

Deserialize a JSON payload of Bungie.net profile information.

Parameters
Returns
def deserialize_profile_item( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.ProfileItemImpl:
432    def deserialize_profile_item(
433        self, payload: typedefs.JSONObject
434    ) -> profile.ProfileItemImpl:
435
436        instance_id: typing.Optional[int] = None
437        if raw_instance_id := payload.get("itemInstanceId"):
438            instance_id = int(raw_instance_id)
439
440        version_number: typing.Optional[int] = None
441        if raw_version := payload.get("versionNumber"):
442            version_number = int(raw_version)
443
444        transfer_status = enums.TransferStatus(payload["transferStatus"])
445
446        return profile.ProfileItemImpl(
447            net=self._net,
448            hash=payload["itemHash"],
449            quantity=payload["quantity"],
450            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
451            location=enums.ItemLocation(payload["location"]),
452            bucket=payload["bucketHash"],
453            transfer_status=transfer_status,
454            lockable=payload["lockable"],
455            state=enums.ItemState(payload["state"]),
456            dismantel_permissions=payload["dismantlePermission"],
457            is_wrapper=payload["isWrapper"],
458            instance_id=instance_id,
459            version_number=version_number,
460            ornament_id=payload.get("overrideStyleItemHash"),
461        )

Deserialize a JSON payload of a singular profile component item.

Parameters
Returns
def deserialize_objectives( self, payload: dict[str, typing.Any]) -> aiobungie.crates.records.Objective:
463    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
464        return records.Objective(
465            net=self._net,
466            hash=payload["objectiveHash"],
467            visible=payload["visible"],
468            complete=payload["complete"],
469            completion_value=payload["completionValue"],
470            progress=payload.get("progress"),
471            destination_hash=payload.get("destinationHash"),
472            activity_hash=payload.get("activityHash"),
473        )

Deserialize a JSON payload of an objective found in a record profile component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
  • aiobungie.crates.records.Objective: A record objective object.
def deserialize_records( self, payload: dict[str, typing.Any], scores: Optional[aiobungie.crates.records.RecordScores] = None, **nodes: int) -> aiobungie.crates.records.Record:
475    def deserialize_records(
476        self,
477        payload: typedefs.JSONObject,
478        scores: typing.Optional[records.RecordScores] = None,
479        **nodes: int,
480    ) -> records.Record:
481        objectives: typing.Optional[list[records.Objective]] = None
482        interval_objectives: typing.Optional[list[records.Objective]] = None
483        record_state: typedefs.IntAnd[records.RecordState]
484
485        record_state = records.RecordState(payload["state"])
486
487        if raw_objs := payload.get("objectives"):
488            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
489
490        if raw_interval_objs := payload.get("intervalObjectives"):
491            interval_objectives = [
492                self.deserialize_objectives(obj) for obj in raw_interval_objs
493            ]
494
495        return records.Record(
496            scores=scores,
497            categories_node_hash=nodes.get("categories_hash", undefined.UNDEFINED),
498            seals_node_hash=nodes.get("seals_hash", undefined.UNDEFINED),
499            state=record_state,
500            objectives=objectives,
501            interval_objectives=interval_objectives,
502            redeemed_count=payload.get("intervalsRedeemedCount", 0),
503            completion_times=payload.get("completedCount", None),
504            reward_visibility=payload.get("rewardVisibilty", None),
505        )

Deserialize a JSON object of a profile record component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
  • scores (typing.Optional[records.RecordScores]): The records scores object. This exists only to keep the signature of aiobungie.crates.CharacterRecord with the record object. As it will always be None in that object.
  • **nodes (int): An int kwargs use to grab the node hashes while deserializing components.
Returns
  • aiobungie.records.Record: A standard implementation of a profile record component.
def deserialize_character_records( self, payload: dict[str, typing.Any], scores: Optional[aiobungie.crates.records.RecordScores] = None, record_hashes: Optional[list[int]] = None) -> aiobungie.crates.records.CharacterRecord:
507    def deserialize_character_records(
508        self,
509        payload: typedefs.JSONObject,
510        scores: typing.Optional[records.RecordScores] = None,
511        record_hashes: typing.Optional[list[int]] = None,
512    ) -> records.CharacterRecord:
513
514        record = self.deserialize_records(payload, scores)
515        return records.CharacterRecord(
516            scores=scores,
517            categories_node_hash=record.categories_node_hash,
518            seals_node_hash=record.seals_node_hash,
519            state=record.state,
520            objectives=record.objectives,
521            interval_objectives=record.interval_objectives,
522            redeemed_count=payload.get("intervalsRedeemedCount", 0),
523            completion_times=payload.get("completedCount"),
524            reward_visibility=payload.get("rewardVisibilty"),
525            record_hashes=record_hashes or [],
526        )

Deserialize a JSON object of a profile character record component.

This almost does the same this as deserialize_records but has more fields which can only be found in a character record.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
Returns
  • aiobungie.records.CharacterRecord: A standard implementation of a profile character record component.
def deserialize_character_dye(self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.Dye:
528    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
529        return character.Dye(
530            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
531        )

Deserialize a JSON payload of a character's dye information.

Parameters
Returns
  • aiobungie.crates.character.Dye: Information about a character dye object.
def deserialize_character_customization( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.CustomizationOptions:
533    def deserialize_character_customization(
534        self, payload: typedefs.JSONObject
535    ) -> character.CustomizationOptions:
536        return character.CustomizationOptions(
537            personality=payload["personality"],
538            face=payload["face"],
539            skin_color=payload["skinColor"],
540            lip_color=payload["lipColor"],
541            eye_color=payload["eyeColor"],
542            hair_colors=payload.get("hairColors", []),
543            feature_colors=payload.get("featureColors", []),
544            decal_color=payload["decalColor"],
545            wear_helmet=payload["wearHelmet"],
546            hair_index=payload["hairIndex"],
547            feature_index=payload["featureIndex"],
548            decal_index=payload["decalIndex"],
549        )

Deserialize a JSON payload of a character customization information found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.CustomizationOptions: Information about a character customs object.
def deserialize_character_minimal_equipments( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.MinimalEquipments:
551    def deserialize_character_minimal_equipments(
552        self, payload: typedefs.JSONObject
553    ) -> character.MinimalEquipments:
554        dyes = None
555        if raw_dyes := payload.get("dyes"):
556            if raw_dyes:
557                dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
558        return character.MinimalEquipments(
559            net=self._net, item_hash=payload["itemHash"], dyes=dyes
560        )

Deserialize a singular JSON peer view of equipment found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.MinimalEquipments: A minimal equipment object.
def deserialize_character_render_data( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.character.RenderedData:
562    def deserialize_character_render_data(
563        self, payload: typedefs.JSONObject, /
564    ) -> character.RenderedData:
565        return character.RenderedData(
566            net=self._net,
567            customization=self.deserialize_character_customization(
568                payload["customization"]
569            ),
570            custom_dyes=[
571                self.deserialize_character_dye(dye)
572                for dye in payload["customDyes"]
573                if dye
574            ],
575            equipment=[
576                self.deserialize_character_minimal_equipments(equipment)
577                for equipment in payload["peerView"]["equipment"]
578            ],
579        )

Deserialize a JSON payload of a profile character render data component.

Parameters
Returns
def deserialize_available_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.AvailableActivity:
581    def deserialize_available_activity(
582        self, payload: typedefs.JSONObject
583    ) -> activity.AvailableActivity:
584        return activity.AvailableActivity(
585            hash=payload["activityHash"],
586            is_new=payload["isNew"],
587            is_completed=payload["isCompleted"],
588            is_visible=payload["isVisible"],
589            display_level=payload.get("displayLevel"),
590            recommended_light=payload.get("recommendedLight"),
591            difficulty=activity.Difficulty(payload["difficultyTier"]),
592            can_join=payload["canJoin"],
593            can_lead=payload["canLead"],
594        )

Deserialize a JSON payload of an available activities.

This method is used to deserialize an array of aiobungie.crates.CharacterActivity.available_activities.

Parameters
Returns
def deserialize_character_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.CharacterActivity:
596    def deserialize_character_activity(
597        self, payload: typedefs.JSONObject
598    ) -> activity.CharacterActivity:
599        current_mode: typing.Optional[enums.GameMode] = None
600        if raw_current_mode := payload.get("currentActivityModeType"):
601            current_mode = enums.GameMode(raw_current_mode)
602
603        current_mode_types: typing.Optional[collections.Sequence[enums.GameMode]] = None
604        if raw_current_modes := payload.get("currentActivityModeTypes"):
605            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
606
607        return activity.CharacterActivity(
608            date_started=time.clean_date(payload["dateActivityStarted"]),
609            current_hash=payload["currentActivityHash"],
610            current_mode_hash=payload["currentActivityModeHash"],
611            current_mode=current_mode,
612            current_mode_hashes=payload.get("currentActivityModeHashes"),
613            current_mode_types=current_mode_types,
614            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
615            last_story_hash=payload["lastCompletedStoryHash"],
616            available_activities=[
617                self.deserialize_available_activity(activity_)
618                for activity_ in payload["availableActivities"]
619            ],
620        )

Deserialize a JSON payload of character activity profile component.

Parameters
Returns
def deserialize_profile_items( self, payload: dict[str, typing.Any], /) -> list[aiobungie.crates.profile.ProfileItemImpl]:
622    def deserialize_profile_items(
623        self, payload: typedefs.JSONObject, /
624    ) -> list[profile.ProfileItemImpl]:
625        return [self.deserialize_profile_item(item) for item in payload["items"]]

Deserialize a JSON payload of profile items component information.

This may deserialize profileInventories or profileCurrencies or any other alternatives.

Parameters
Returns
def deserialize_progressions( self, payload: dict[str, typing.Any]) -> aiobungie.crates.progressions.Progression:
668    def deserialize_progressions(
669        self, payload: typedefs.JSONObject
670    ) -> progressions.Progression:
671        return progressions.Progression(
672            hash=int(payload["progressionHash"]),
673            level=int(payload["level"]),
674            cap=int(payload["levelCap"]),
675            daily_limit=int(payload["dailyLimit"]),
676            weekly_limit=int(payload["weeklyLimit"]),
677            current_progress=int(payload["currentProgress"]),
678            daily_progress=int(payload["dailyProgress"]),
679            needed=int(payload["progressToNextLevel"]),
680            next_level=int(payload["nextLevelAt"]),
681        )
def deserialize_milestone( self, payload: dict[str, typing.Any]) -> aiobungie.crates.milestones.Milestone:
769    def deserialize_milestone(
770        self, payload: typedefs.JSONObject
771    ) -> milestones.Milestone:
772        start_date: typing.Optional[datetime.datetime] = None
773        if raw_start_date := payload.get("startDate"):
774            start_date = time.clean_date(raw_start_date)
775
776        end_date: typing.Optional[datetime.datetime] = None
777        if raw_end_date := payload.get("endDate"):
778            end_date = time.clean_date(raw_end_date)
779
780        rewards: typing.Optional[
781            collections.Collection[milestones.MilestoneReward]
782        ] = None
783        if raw_rewards := payload.get("rewards"):
784            rewards = [
785                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
786            ]
787
788        activities: typing.Optional[
789            collections.Sequence[milestones.MilestoneActivity]
790        ] = None
791        if raw_activities := payload.get("activities"):
792            activities = [
793                self._deserialize_milestone_activity(active)
794                for active in raw_activities
795            ]
796
797        quests: typing.Optional[collections.Sequence[milestones.MilestoneQuest]] = None
798        if raw_quests := payload.get("availableQuests"):
799            quests = [
800                self._deserialize_milestone_available_quest(quest)
801                for quest in raw_quests
802            ]
803
804        vendors: typing.Optional[
805            collections.Sequence[milestones.MilestoneVendor]
806        ] = None
807        if raw_vendors := payload.get("vendors"):
808            vendors = [
809                milestones.MilestoneVendor(
810                    vendor_hash=vendor["vendorHash"],
811                    preview_itemhash=vendor.get("previewItemHash"),
812                )
813                for vendor in raw_vendors
814            ]
815
816        return milestones.Milestone(
817            hash=payload["milestoneHash"],
818            start_date=start_date,
819            end_date=end_date,
820            order=payload["order"],
821            rewards=rewards,
822            available_quests=quests,
823            activities=activities,
824            vendors=vendors,
825        )
def deserialize_characters( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.Character]:
842    def deserialize_characters(
843        self, payload: typedefs.JSONObject
844    ) -> collections.Mapping[int, character.Character]:
845        return {
846            int(char_id): self._set_character_attrs(char)
847            for char_id, char in payload["data"].items()
848        }
def deserialize_character( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.Character:
850    def deserialize_character(
851        self, payload: typedefs.JSONObject
852    ) -> character.Character:
853        return self._set_character_attrs(payload)
def deserialize_character_equipments( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, collections.abc.Sequence[aiobungie.crates.profile.ProfileItemImpl]]:
855    def deserialize_character_equipments(
856        self, payload: typedefs.JSONObject
857    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
858        return {
859            int(char_id): self.deserialize_profile_items(item)
860            for char_id, item in payload["data"].items()
861        }
def deserialize_character_activities( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.activity.CharacterActivity]:
863    def deserialize_character_activities(
864        self, payload: typedefs.JSONObject
865    ) -> collections.Mapping[int, activity.CharacterActivity]:
866        return {
867            int(char_id): self.deserialize_character_activity(data)
868            for char_id, data in payload["data"].items()
869        }
def deserialize_characters_render_data( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.RenderedData]:
871    def deserialize_characters_render_data(
872        self, payload: typedefs.JSONObject
873    ) -> collections.Mapping[int, character.RenderedData]:
874        return {
875            int(char_id): self.deserialize_character_render_data(data)
876            for char_id, data in payload["data"].items()
877        }
def deserialize_character_progressions( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.CharacterProgression:
879    def deserialize_character_progressions(
880        self, payload: typedefs.JSONObject
881    ) -> character.CharacterProgression:
882        progressions_ = {
883            int(prog_id): self.deserialize_progressions(prog)
884            for prog_id, prog in payload["progressions"].items()
885        }
886
887        factions = {
888            int(faction_id): self._deserialize_factions(faction)
889            for faction_id, faction in payload["factions"].items()
890        }
891
892        milestones_ = {
893            int(milestone_hash): self.deserialize_milestone(milestone)
894            for milestone_hash, milestone in payload["milestones"].items()
895        }
896
897        uninstanced_item_objectives = {
898            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
899            for item_hash, obj in payload["uninstancedItemObjectives"].items()
900        }
901
902        artifact = payload["seasonalArtifact"]
903        seasonal_artifact = season.CharacterScopedArtifact(
904            hash=artifact["artifactHash"],
905            points_used=artifact["pointsUsed"],
906            reset_count=artifact["resetCount"],
907            tiers=[
908                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
909            ],
910        )
911        checklists = payload["checklists"]
912
913        return character.CharacterProgression(
914            progressions=progressions_,
915            factions=factions,
916            checklists=checklists,
917            milestones=milestones_,
918            seasonal_artifact=seasonal_artifact,
919            uninstanced_item_objectives=uninstanced_item_objectives,
920        )
def deserialize_character_progressions_mapping( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.CharacterProgression]:
922    def deserialize_character_progressions_mapping(
923        self, payload: typedefs.JSONObject
924    ) -> collections.Mapping[int, character.CharacterProgression]:
925        character_progressions: collections.Mapping[
926            int, character.CharacterProgression
927        ] = {}
928        for char_id, data in payload["data"].items():
929            # A little hack to stop mypy complaining about Mapping <-> dict
930            character_progressions[int(char_id)] = self.deserialize_character_progressions(data)  # type: ignore[index]
931        return character_progressions
def deserialize_characters_records( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.CharacterRecord]:
933    def deserialize_characters_records(
934        self,
935        payload: typedefs.JSONObject,
936    ) -> collections.Mapping[int, records.CharacterRecord]:
937
938        return {
939            int(rec_id): self.deserialize_character_records(
940                rec, record_hashes=payload.get("featuredRecordHashes")
941            )
942            for rec_id, rec in payload["records"].items()
943        }
def deserialize_profile_records( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.Record]:
945    def deserialize_profile_records(
946        self, payload: typedefs.JSONObject
947    ) -> collections.Mapping[int, records.Record]:
948        raw_profile_records = payload["data"]
949        scores = records.RecordScores(
950            current_score=raw_profile_records["score"],
951            legacy_score=raw_profile_records["legacyScore"],
952            lifetime_score=raw_profile_records["lifetimeScore"],
953        )
954        return {
955            int(record_id): self.deserialize_records(
956                record,
957                scores,
958                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
959                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
960            )
961            for record_id, record in raw_profile_records["records"].items()
962        }
def deserialize_craftables_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.CraftablesComponent:
 999    def deserialize_craftables_component(
1000        self, payload: typedefs.JSONObject
1001    ) -> components.CraftablesComponent:
1002        return components.CraftablesComponent(
1003            net=self._net,
1004            craftables={
1005                int(item_id): self._deserialize_craftable_item(item)
1006                for item_id, item in payload["craftables"].items()
1007                if item is not None
1008            },
1009            crafting_root_node_hash=payload["craftingRootNodeHash"],
1010        )
def deserialize_components( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.Component:
1012    def deserialize_components(  # noqa: C901 Too complex.
1013        self, payload: typedefs.JSONObject
1014    ) -> components.Component:
1015
1016        profile_: typing.Optional[profile.Profile] = None
1017        if raw_profile := payload.get("profile"):
1018            profile_ = self.deserialize_profile(raw_profile)
1019
1020        profile_progression: typing.Optional[profile.ProfileProgression] = None
1021        if raw_profile_progression := payload.get("profileProgression"):
1022            profile_progression = self.deserialize_profile_progression(
1023                raw_profile_progression
1024            )
1025
1026        profile_currencies: typing.Optional[
1027            collections.Sequence[profile.ProfileItemImpl]
1028        ] = None
1029        if raw_profile_currencies := payload.get("profileCurrencies"):
1030            if "data" in raw_profile_currencies:
1031                profile_currencies = self.deserialize_profile_items(
1032                    raw_profile_currencies["data"]
1033                )
1034
1035        profile_inventories: typing.Optional[
1036            collections.Sequence[profile.ProfileItemImpl]
1037        ] = None
1038        if raw_profile_inventories := payload.get("profileInventory"):
1039            if "data" in raw_profile_inventories:
1040                profile_inventories = self.deserialize_profile_items(
1041                    raw_profile_inventories["data"]
1042                )
1043
1044        profile_records: typing.Optional[
1045            collections.Mapping[int, records.Record]
1046        ] = None
1047
1048        if raw_profile_records_ := payload.get("profileRecords"):
1049            profile_records = self.deserialize_profile_records(raw_profile_records_)
1050
1051        characters: typing.Optional[typing.Mapping[int, character.Character]] = None
1052        if raw_characters := payload.get("characters"):
1053            characters = self.deserialize_characters(raw_characters)
1054
1055        character_records: typing.Optional[
1056            collections.Mapping[int, records.CharacterRecord]
1057        ] = None
1058
1059        if raw_character_records := payload.get("characterRecords"):
1060            # Had to do it in two steps..
1061            to_update: typedefs.JSONObject = {}
1062            for _, data in raw_character_records["data"].items():
1063                for record_id, record in data.items():
1064                    to_update[record_id] = record
1065
1066            character_records = {
1067                int(rec_id): self.deserialize_character_records(
1068                    rec, record_hashes=to_update.get("featuredRecordHashes")
1069                )
1070                for rec_id, rec in to_update["records"].items()
1071            }
1072
1073        character_equipments: typing.Optional[
1074            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1075        ] = None
1076        if raw_character_equips := payload.get("characterEquipment"):
1077            character_equipments = self.deserialize_character_equipments(
1078                raw_character_equips
1079            )
1080
1081        character_inventories: typing.Optional[
1082            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1083        ] = None
1084        if raw_character_inventories := payload.get("characterInventories"):
1085            if "data" in raw_character_inventories:
1086                character_inventories = self.deserialize_character_equipments(
1087                    raw_character_inventories
1088                )
1089
1090        character_activities: typing.Optional[
1091            collections.Mapping[int, activity.CharacterActivity]
1092        ] = None
1093        if raw_char_acts := payload.get("characterActivities"):
1094            character_activities = self.deserialize_character_activities(raw_char_acts)
1095
1096        character_render_data: typing.Optional[
1097            collections.Mapping[int, character.RenderedData]
1098        ] = None
1099        if raw_character_render_data := payload.get("characterRenderData"):
1100            character_render_data = self.deserialize_characters_render_data(
1101                raw_character_render_data
1102            )
1103
1104        character_progressions: typing.Optional[
1105            collections.Mapping[int, character.CharacterProgression]
1106        ] = None
1107
1108        if raw_character_progressions := payload.get("characterProgressions"):
1109            character_progressions = self.deserialize_character_progressions_mapping(
1110                raw_character_progressions
1111            )
1112
1113        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1114        if raw_profile_string_vars := payload.get("profileStringVariables"):
1115            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1116
1117        character_string_vars: typing.Optional[
1118            collections.Mapping[int, collections.Mapping[int, int]]
1119        ] = None
1120        if raw_character_string_vars := payload.get("characterStringVariables"):
1121            character_string_vars = {
1122                int(char_id): data["integerValuesByHash"]
1123                for char_id, data in raw_character_string_vars["data"].items()
1124            }
1125
1126        metrics: typing.Optional[
1127            collections.Sequence[
1128                collections.Mapping[
1129                    int, tuple[bool, typing.Optional[records.Objective]]
1130                ]
1131            ]
1132        ] = None
1133        root_node_hash: typing.Optional[int] = None
1134
1135        if raw_metrics := payload.get("metrics"):
1136            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1137            metrics = [
1138                {
1139                    int(metrics_hash): (
1140                        data["invisible"],
1141                        self.deserialize_objectives(data["objectiveProgress"])
1142                        if "objectiveProgress" in data
1143                        else None,
1144                    )
1145                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1146                }
1147            ]
1148        transitory: typing.Optional[fireteams.FireteamParty] = None
1149        if raw_transitory := payload.get("profileTransitoryData"):
1150            if "data" in raw_transitory:
1151                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1152
1153        item_components: typing.Optional[components.ItemsComponent] = None
1154        if raw_item_components := payload.get("itemComponents"):
1155            item_components = self.deserialize_items_component(raw_item_components)
1156
1157        profile_plugsets: typing.Optional[
1158            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1159        ] = None
1160
1161        if raw_profile_plugs := payload.get("profilePlugSets"):
1162            profile_plugsets = {
1163                int(index): [self.deserialize_plug_item_state(state) for state in data]
1164                for index, data in raw_profile_plugs["data"]["plugs"].items()
1165            }
1166
1167        character_plugsets: typing.Optional[
1168            collections.Mapping[
1169                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1170            ]
1171        ] = None
1172        if raw_char_plugsets := payload.get("characterPlugSets"):
1173            character_plugsets = {
1174                int(char_id): {
1175                    int(index): [
1176                        self.deserialize_plug_item_state(state) for state in data
1177                    ]
1178                    for index, data in inner["plugs"].items()
1179                }
1180                for char_id, inner in raw_char_plugsets["data"].items()
1181            }
1182
1183        character_collectibles: typing.Optional[
1184            collections.Mapping[int, items.Collectible]
1185        ] = None
1186        if raw_character_collectibles := payload.get("characterCollectibles"):
1187            character_collectibles = {
1188                int(char_id): self._deserialize_collectible(data)
1189                for char_id, data in raw_character_collectibles["data"].items()
1190            }
1191
1192        profile_collectibles: typing.Optional[items.Collectible] = None
1193        if raw_profile_collectibles := payload.get("profileCollectibles"):
1194            profile_collectibles = self._deserialize_collectible(
1195                raw_profile_collectibles["data"]
1196            )
1197
1198        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1199        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1200            profile_nodes = {
1201                int(node_hash): self._deserialize_node(node)
1202                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1203            }
1204
1205        character_nodes: typing.Optional[
1206            collections.Mapping[int, collections.Mapping[int, records.Node]]
1207        ] = None
1208        if raw_character_nodes := payload.get("characterPresentationNodes"):
1209            character_nodes = {
1210                int(char_id): {
1211                    int(node_hash): self._deserialize_node(node)
1212                    for node_hash, node in each_character["nodes"].items()
1213                }
1214                for char_id, each_character in raw_character_nodes["data"].items()
1215            }
1216
1217        platform_silver: typing.Optional[
1218            collections.Mapping[str, profile.ProfileItemImpl]
1219        ] = None
1220        if raw_platform_silver := payload.get("platformSilver"):
1221            if "data" in raw_platform_silver:
1222                platform_silver = {
1223                    platform_name: self.deserialize_profile_item(item)
1224                    for platform_name, item in raw_platform_silver["data"][
1225                        "platformSilver"
1226                    ].items()
1227                }
1228
1229        character_currency_lookups: typing.Optional[
1230            collections.Mapping[int, collections.Sequence[items.Currency]]
1231        ] = None
1232        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1233            if "data" in raw_char_lookups:
1234                character_currency_lookups = {
1235                    int(char_id): self._deserialize_currencies(currencie)
1236                    for char_id, currencie in raw_char_lookups["data"].items()
1237                }
1238
1239        character_craftables: typing.Optional[
1240            collections.Mapping[int, components.CraftablesComponent]
1241        ] = None
1242        if raw_character_craftables := payload.get("characterCraftables"):
1243
1244            if "data" in raw_character_craftables:
1245                character_craftables = {
1246                    int(char_id): self.deserialize_craftables_component(craftable)
1247                    for char_id, craftable in raw_character_craftables["data"].items()
1248                }
1249
1250        return components.Component(
1251            profiles=profile_,
1252            profile_progression=profile_progression,
1253            profile_currencies=profile_currencies,
1254            profile_inventories=profile_inventories,
1255            profile_records=profile_records,
1256            characters=characters,
1257            character_records=character_records,
1258            character_equipments=character_equipments,
1259            character_inventories=character_inventories,
1260            character_activities=character_activities,
1261            character_render_data=character_render_data,
1262            character_progressions=character_progressions,
1263            profile_string_variables=profile_string_vars,
1264            character_string_variables=character_string_vars,
1265            metrics=metrics,
1266            root_node_hash=root_node_hash,
1267            transitory=transitory,
1268            item_components=item_components,
1269            profile_plugsets=profile_plugsets,
1270            character_plugsets=character_plugsets,
1271            character_collectibles=character_collectibles,
1272            profile_collectibles=profile_collectibles,
1273            profile_nodes=profile_nodes,
1274            character_nodes=character_nodes,
1275            platform_silver=platform_silver,
1276            character_currency_lookups=character_currency_lookups,
1277            character_craftables=character_craftables,
1278        )

Deserialize a JSON payload of Bungie.net profile components information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_items_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.ItemsComponent:
1280    def deserialize_items_component(
1281        self, payload: typedefs.JSONObject
1282    ) -> components.ItemsComponent:
1283        instances: typing.Optional[
1284            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1285        ] = None
1286        if raw_instances := payload.get("instances"):
1287            instances = [
1288                {
1289                    int(ins_id): self.deserialize_instanced_item(item)
1290                    for ins_id, item in raw_instances["data"].items()
1291                }
1292            ]
1293
1294        render_data: typing.Optional[
1295            collections.Mapping[int, tuple[bool, dict[int, int]]]
1296        ] = None
1297        if raw_render_data := payload.get("renderData"):
1298            render_data = {
1299                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1300                for ins_id, data in raw_render_data["data"].items()
1301            }
1302
1303        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1304        if raw_stats := payload.get("stats"):
1305            builder: collections.Mapping[int, items.ItemStatsView] = {}
1306            for ins_id, stat in raw_stats["data"].items():
1307                for _, items_ in stat.items():
1308                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1309            stats = builder
1310
1311        sockets: typing.Optional[
1312            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1313        ] = None
1314        if raw_sockets := payload.get("sockets"):
1315            sockets = {
1316                int(ins_id): [
1317                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1318                ]
1319                for ins_id, item in raw_sockets["data"].items()
1320            }
1321
1322        objeectives: typing.Optional[
1323            collections.Mapping[int, collections.Sequence[records.Objective]]
1324        ] = None
1325        if raw_objectives := payload.get("objectives"):
1326            objeectives = {
1327                int(ins_id): [self.deserialize_objectives(objective)]
1328                for ins_id, data in raw_objectives["data"].items()
1329                for objective in data["objectives"]
1330            }
1331
1332        perks: typing.Optional[
1333            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1334        ] = None
1335        if raw_perks := payload.get("perks"):
1336            perks = {
1337                int(ins_id): [
1338                    self.deserialize_item_perk(perk) for perk in item["perks"]
1339                ]
1340                for ins_id, item in raw_perks["data"].items()
1341            }
1342
1343        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1344        if raw_plug_states := payload.get("plugStates"):
1345            pending_states: list[items.PlugItemState] = []
1346            for _, plug in raw_plug_states["data"].items():
1347                pending_states.append(self.deserialize_plug_item_state(plug))
1348            plug_states = pending_states
1349
1350        reusable_plugs: typing.Optional[
1351            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1352        ] = None
1353        if raw_re_plugs := payload.get("reusablePlugs"):
1354            reusable_plugs = {
1355                int(ins_id): [
1356                    self.deserialize_plug_item_state(state) for state in inner
1357                ]
1358                for ins_id, plug in raw_re_plugs["data"].items()
1359                for inner in list(plug["plugs"].values())
1360            }
1361
1362        plug_objectives: typing.Optional[
1363            collections.Mapping[
1364                int, collections.Mapping[int, collections.Collection[records.Objective]]
1365            ]
1366        ] = None
1367        if raw_plug_objectives := payload.get("plugObjectives"):
1368            plug_objectives = {
1369                int(ins_id): {
1370                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1371                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1372                }
1373                for ins_id, inner in raw_plug_objectives["data"].items()
1374            }
1375
1376        return components.ItemsComponent(
1377            sockets=sockets,
1378            stats=stats,
1379            render_data=render_data,
1380            instances=instances,
1381            objectives=objeectives,
1382            perks=perks,
1383            plug_states=plug_states,
1384            reusable_plugs=reusable_plugs,
1385            plug_objectives=plug_objectives,
1386        )

Deserialize a JSON objects within the itemComponents key.`

def deserialize_character_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.CharacterComponent:
1388    def deserialize_character_component(  # type: ignore[call-arg]
1389        self, payload: typedefs.JSONObject
1390    ) -> components.CharacterComponent:
1391
1392        character_: typing.Optional[character.Character] = None
1393        if raw_singuler_character := payload.get("character"):
1394            character_ = self.deserialize_character(raw_singuler_character["data"])
1395
1396        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1397        if raw_inventory := payload.get("inventory"):
1398            if "data" in raw_inventory:
1399                inventory = self.deserialize_profile_items(raw_inventory["data"])
1400
1401        activities: typing.Optional[activity.CharacterActivity] = None
1402        if raw_activities := payload.get("activities"):
1403            activities = self.deserialize_character_activity(raw_activities["data"])
1404
1405        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1406        if raw_equipments := payload.get("equipment"):
1407            equipment = self.deserialize_profile_items(raw_equipments["data"])
1408
1409        progressions_: typing.Optional[character.CharacterProgression] = None
1410        if raw_progressions := payload.get("progressions"):
1411            progressions_ = self.deserialize_character_progressions(
1412                raw_progressions["data"]
1413            )
1414
1415        render_data: typing.Optional[character.RenderedData] = None
1416        if raw_render_data := payload.get("renderData"):
1417            render_data = self.deserialize_character_render_data(
1418                raw_render_data["data"]
1419            )
1420
1421        character_records: typing.Optional[
1422            collections.Mapping[int, records.CharacterRecord]
1423        ] = None
1424        if raw_char_records := payload.get("records"):
1425            character_records = self.deserialize_characters_records(
1426                raw_char_records["data"]
1427            )
1428
1429        item_components: typing.Optional[components.ItemsComponent] = None
1430        if raw_item_components := payload.get("itemComponents"):
1431            item_components = self.deserialize_items_component(raw_item_components)
1432
1433        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1434        if raw_nodes := payload.get("presentationNodes"):
1435            nodes = {
1436                int(node_hash): self._deserialize_node(node)
1437                for node_hash, node in raw_nodes["data"]["nodes"].items()
1438            }
1439
1440        collectibles: typing.Optional[items.Collectible] = None
1441        if raw_collectibles := payload.get("collectibles"):
1442            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1443
1444        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1445        if raw_currencies := payload.get("currencyLookups"):
1446            if "data" in raw_currencies:
1447                currency_lookups = self._deserialize_currencies(raw_currencies)
1448
1449        return components.CharacterComponent(
1450            activities=activities,
1451            equipment=equipment,
1452            inventory=inventory,
1453            progressions=progressions_,
1454            render_data=render_data,
1455            character=character_,
1456            character_records=character_records,
1457            profile_records=None,
1458            item_components=item_components,
1459            currency_lookups=currency_lookups,
1460            collectibles=collectibles,
1461            nodes=nodes,
1462        )

Deserialize a JSON payload of Destiny 2 character component.

Parameters
Returns
def deserialize_inventory_results( self, payload: dict[str, typing.Any]) -> aiobungie.Iterator[aiobungie.crates.entity.SearchableEntity]:
1490    def deserialize_inventory_results(
1491        self, payload: typedefs.JSONObject
1492    ) -> iterators.Iterator[entity.SearchableEntity]:
1493        suggested_words: list[str] = payload["suggestedWords"]
1494
1495        def _check_unknown(s: str) -> undefined.UndefinedOr[str]:
1496            return s if not typedefs.is_unknown(s) else undefined.UNDEFINED
1497
1498        return iterators.Iterator(
1499            [
1500                entity.SearchableEntity(
1501                    net=self._net,
1502                    hash=data["hash"],
1503                    entity_type=data["entityType"],
1504                    weight=data["weight"],
1505                    suggested_words=suggested_words,
1506                    name=data["displayProperties"]["name"],
1507                    has_icon=data["displayProperties"]["hasIcon"],
1508                    description=_check_unknown(
1509                        data["displayProperties"]["description"]
1510                    ),
1511                    icon=assets.Image(data["displayProperties"]["icon"]),
1512                )
1513                for data in payload["results"]["results"]
1514            ]
1515        )

Deserialize results of searched Destiny2 entities.

Parameters
Returns
def deserialize_inventory_entity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.entity.InventoryEntity:
1544    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1545        self, payload: typedefs.JSONObject, /
1546    ) -> entity.InventoryEntity:
1547
1548        props = self._set_entity_attrs(payload)
1549        objects = self._deserialize_inventory_item_objects(payload)
1550
1551        collectible_hash: typing.Optional[int] = None
1552        if raw_collectible_hash := payload.get("collectibleHash"):
1553            collectible_hash = int(raw_collectible_hash)
1554
1555        secondary_icon: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1556        if raw_second_icon := payload.get("secondaryIcon"):
1557            secondary_icon = assets.Image(raw_second_icon)
1558
1559        secondary_overlay: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1560        if raw_second_overlay := payload.get("secondaryOverlay"):
1561            secondary_overlay = assets.Image(raw_second_overlay)
1562
1563        secondary_special: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1564        if raw_second_special := payload.get("secondarySpecial"):
1565            secondary_special = assets.Image(raw_second_special)
1566
1567        screenshot: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1568        if raw_screenshot := payload.get("screenshot"):
1569            screenshot = assets.Image(raw_screenshot)
1570
1571        watermark_icon: typing.Optional[assets.Image] = None
1572        if raw_watermark_icon := payload.get("iconWatermark"):
1573            watermark_icon = assets.Image(raw_watermark_icon)
1574
1575        watermark_shelved: typing.Optional[assets.Image] = None
1576        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1577            watermark_shelved = assets.Image(raw_watermark_shelved)
1578
1579        about: undefined.UndefinedOr[str] = undefined.UNDEFINED
1580        if (raw_about := payload.get("flavorText")) and not typedefs.is_unknown(
1581            raw_about
1582        ):
1583            about = raw_about
1584
1585        ui_item_style: undefined.UndefinedOr[str] = undefined.UNDEFINED
1586        if (
1587            raw_ui_style := payload.get("uiItemDisplayStyle")
1588        ) and not typedefs.is_unknown(raw_ui_style):
1589            ui_item_style = raw_ui_style
1590
1591        tier_and_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1592        if (
1593            raw_tier_and_name := payload.get("itemTypeAndTierDisplayName")
1594        ) and not typedefs.is_unknown(raw_tier_and_name):
1595            tier_and_name = raw_tier_and_name
1596
1597        type_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1598        if (
1599            raw_type_name := payload.get("itemTypeDisplayName")
1600        ) and not typedefs.is_unknown(raw_type_name):
1601            type_name = raw_type_name
1602
1603        display_source: undefined.UndefinedOr[str] = undefined.UNDEFINED
1604        if (
1605            raw_display_source := payload.get("displaySource")
1606        ) and not typedefs.is_unknown(raw_display_source):
1607            display_source = raw_display_source
1608
1609        lorehash: typing.Optional[int] = None
1610        if raw_lore_hash := payload.get("loreHash"):
1611            lorehash = int(raw_lore_hash)
1612
1613        summary_hash: typing.Optional[int] = None
1614        if raw_summary_hash := payload.get("summaryItemHash"):
1615            summary_hash = raw_summary_hash
1616
1617        breaker_type_hash: typing.Optional[int] = None
1618        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1619            breaker_type_hash = int(raw_breaker_type_hash)
1620
1621        damage_types: typing.Optional[collections.Sequence[int]] = None
1622        if raw_damage_types := payload.get("damageTypes"):
1623            damage_types = [int(type_) for type_ in raw_damage_types]
1624
1625        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1626        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1627            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1628
1629        default_damagetype_hash: typing.Optional[int] = None
1630        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1631            default_damagetype_hash = int(raw_defaultdmg_hash)
1632
1633        emblem_objective_hash: typing.Optional[int] = None
1634        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1635            emblem_objective_hash = int(raw_emblem_obj_hash)
1636
1637        tier_type: typing.Optional[enums.TierType] = None
1638        tier: typing.Optional[enums.ItemTier] = None
1639        bucket_hash: typing.Optional[int] = None
1640        recovery_hash: typing.Optional[int] = None
1641        tier_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1642        isinstance_item: bool = False
1643        expire_tool_tip: undefined.UndefinedOr[str] = undefined.UNDEFINED
1644        expire_in_orbit_message: undefined.UndefinedOr[str] = undefined.UNDEFINED
1645        suppress_expiration: bool = False
1646        max_stack_size: typing.Optional[int] = None
1647        stack_label: undefined.UndefinedOr[str] = undefined.UNDEFINED
1648
1649        if inventory := payload.get("inventory"):
1650            tier_type = enums.TierType(int(inventory["tierType"]))
1651            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1652            bucket_hash = int(inventory["bucketTypeHash"])
1653            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1654            tier_name = inventory["tierTypeName"]
1655            isinstance_item = inventory["isInstanceItem"]
1656            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1657            max_stack_size = int(inventory["maxStackSize"])
1658
1659            try:
1660                stack_label = inventory["stackUniqueLabel"]
1661            except KeyError:
1662                pass
1663
1664        return entity.InventoryEntity(
1665            net=self._net,
1666            collectible_hash=collectible_hash,
1667            name=props.name,
1668            about=about,
1669            emblem_objective_hash=emblem_objective_hash,
1670            suppress_expiration=suppress_expiration,
1671            max_stack_size=max_stack_size,
1672            stack_label=stack_label,
1673            tier=tier,
1674            tier_type=tier_type,
1675            tier_name=tier_name,
1676            bucket_hash=bucket_hash,
1677            recovery_bucket_hash=recovery_hash,
1678            isinstance_item=isinstance_item,
1679            expire_in_orbit_message=expire_in_orbit_message,
1680            expiration_tooltip=expire_tool_tip,
1681            lore_hash=lorehash,
1682            type_and_tier_name=tier_and_name,
1683            summary_hash=summary_hash,
1684            ui_display_style=ui_item_style,
1685            type_name=type_name,
1686            breaker_type_hash=breaker_type_hash,
1687            description=props.description,
1688            display_source=display_source,
1689            hash=props.hash,
1690            damage_types=damage_types,
1691            index=props.index,
1692            icon=props.icon,
1693            has_icon=props.has_icon,
1694            screenshot=screenshot,
1695            watermark_icon=watermark_icon,
1696            watermark_shelved=watermark_shelved,
1697            secondary_icon=secondary_icon,
1698            secondary_overlay=secondary_overlay,
1699            secondary_special=secondary_special,
1700            type=enums.ItemType(int(payload["itemType"])),
1701            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1702            trait_ids=[trait for trait in payload.get("traitIds", [])],
1703            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1704            item_class=enums.Class(int(payload["classType"])),
1705            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1706            breaker_type=int(payload["breakerType"]),
1707            default_damagetype=int(payload["defaultDamageType"]),
1708            default_damagetype_hash=default_damagetype_hash,
1709            damagetype_hashes=damagetype_hashes,
1710            tooltip_notifications=payload["tooltipNotifications"],
1711            not_transferable=payload["nonTransferrable"],
1712            allow_actions=payload["allowActions"],
1713            is_equippable=payload["equippable"],
1714            objects=objects,
1715            background_colors=payload.get("backgroundColor", {}),
1716            season_hash=payload.get("seasonHash"),
1717            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1718        )

Deserialize a JSON payload of an inventory entity item information.

This can be any item from DestinyInventoryItemDefinition definition.

Parameters
Returns
def deserialize_objective_entity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.entity.ObjectiveEntity:
1720    def deserialize_objective_entity(
1721        self, payload: typedefs.JSONObject, /
1722    ) -> entity.ObjectiveEntity:
1723        props = self._set_entity_attrs(payload)
1724        return entity.ObjectiveEntity(
1725            net=self._net,
1726            hash=props.hash,
1727            index=props.index,
1728            description=props.description,
1729            name=props.name,
1730            has_icon=props.has_icon,
1731            icon=props.icon,
1732            unlock_value_hash=payload["unlockValueHash"],
1733            completion_value=payload["completionValue"],
1734            scope=entity.GatingScope(int(payload["scope"])),
1735            location_hash=payload["locationHash"],
1736            allowed_negative_value=payload["allowNegativeValue"],
1737            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1738            counting_downward=payload["isCountingDownward"],
1739            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1740            progress_description=payload["progressDescription"],
1741            perks=payload["perks"],
1742            stats=payload["stats"],
1743            minimum_visibility=payload["minimumVisibilityThreshold"],
1744            allow_over_completion=payload["allowOvercompletion"],
1745            show_value_style=payload["showValueOnComplete"],
1746            display_only_objective=payload["isDisplayOnlyObjective"],
1747            complete_value_style=entity.ValueUIStyle(
1748                int(payload["completedValueStyle"])
1749            ),
1750            progress_value_style=entity.ValueUIStyle(
1751                int(payload["inProgressValueStyle"])
1752            ),
1753            ui_label=payload["uiLabel"],
1754            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1755        )

Deserialize a JSON payload of an objective entity information.

Parameters
Returns
def deserialize_activity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.activity.Activity:
1783    def deserialize_activity(
1784        self,
1785        payload: typedefs.JSONObject,
1786        /,
1787    ) -> activity.Activity:
1788        period = time.clean_date(payload["period"])
1789        details = payload["activityDetails"]
1790        ref_id = int(details["referenceId"])
1791        instance_id = int(details["instanceId"])
1792        mode = enums.GameMode(details["mode"])
1793        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1794        is_private = details["isPrivate"]
1795        membership_type = enums.MembershipType(int(details["membershipType"]))
1796
1797        # Since we're using the same fields for post activity method
1798        # this check is required since post activity doesn't values values
1799        values = self._deserialize_activity_values(payload["values"])
1800
1801        return activity.Activity(
1802            net=self._net,
1803            hash=ref_id,
1804            instance_id=instance_id,
1805            mode=mode,
1806            modes=modes,
1807            is_private=is_private,
1808            membership_type=membership_type,
1809            occurred_at=period,
1810            values=values,
1811        )

Deserialize a JSON payload of an activity history information.

Parameters
Returns
def deserialize_activities( self, payload: dict[str, typing.Any]) -> aiobungie.Iterator[aiobungie.crates.activity.Activity]:
1813    def deserialize_activities(
1814        self, payload: typedefs.JSONObject
1815    ) -> iterators.Iterator[activity.Activity]:
1816        return iterators.Iterator(
1817            [
1818                self.deserialize_activity(activity_)
1819                for activity_ in payload["activities"]
1820            ]
1821        )

Deserialize a JSON payload of an array of activity history information.

Parameters
Returns
def deserialize_extended_weapon_values( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.ExtendedWeaponValues:
1823    def deserialize_extended_weapon_values(
1824        self, payload: typedefs.JSONObject
1825    ) -> activity.ExtendedWeaponValues:
1826
1827        assists: typing.Optional[int] = None
1828        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1829            assists = raw_assists["basic"]["value"]
1830        assists_damage: typing.Optional[int] = None
1831
1832        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1833            assists_damage = raw_assists_damage["basic"]["value"]
1834
1835        return activity.ExtendedWeaponValues(
1836            reference_id=int(payload["referenceId"]),
1837            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1838            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1839                "value"
1840            ],
1841            assists=assists,
1842            assists_damage=assists_damage,
1843            precision_kills_percentage=(
1844                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1845                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1846                    "displayValue"
1847                ],
1848            ),
1849        )

Deserialize values of extended weapons JSON object.

Parameters
Returns
def deserialize_post_activity_player( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.activity.PostActivityPlayer:
1872    def deserialize_post_activity_player(
1873        self, payload: typedefs.JSONObject, /
1874    ) -> activity.PostActivityPlayer:
1875        player = payload["player"]
1876
1877        class_hash: typedefs.NoneOr[int] = None
1878        if (class_hash := player.get("classHash")) is not None:
1879            class_hash = class_hash
1880
1881        race_hash: typedefs.NoneOr[int] = None
1882        if (race_hash := player.get("raceHash")) is not None:
1883            race_hash = race_hash
1884
1885        gender_hash: typedefs.NoneOr[int] = None
1886        if (gender_hash := player.get("genderHash")) is not None:
1887            gender_hash = gender_hash
1888
1889        character_class: undefined.UndefinedOr[str] = undefined.UNDEFINED
1890        if (
1891            character_class := player.get("characterClass")
1892        ) and not typedefs.is_unknown(character_class):
1893            character_class = character_class
1894
1895        character_level: typedefs.NoneOr[int] = None
1896        if (character_level := player.get("characterLevel")) is not None:
1897            character_level = character_level
1898
1899        return activity.PostActivityPlayer(
1900            standing=int(payload["standing"]),
1901            score=int(payload["score"]["basic"]["value"]),
1902            character_id=payload["characterId"],
1903            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1904            character_class=character_class,
1905            character_level=character_level,
1906            race_hash=race_hash,
1907            gender_hash=gender_hash,
1908            class_hash=class_hash,
1909            light_level=int(player["lightLevel"]),
1910            emblem_hash=int(player["emblemHash"]),
1911            values=self._deserialize_activity_values(payload["values"]),
1912            extended_values=self._deserialize_extended_values(payload["extended"]),
1913        )

Deserialize a JSON payload of a post activity player information.

Parameters
Returns
def deserialize_post_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.PostActivity:
1925    def deserialize_post_activity(
1926        self, payload: typedefs.JSONObject
1927    ) -> activity.PostActivity:
1928        period = time.clean_date(payload["period"])
1929        details = payload["activityDetails"]
1930        ref_id = int(details["referenceId"])
1931        instance_id = int(details["instanceId"])
1932        mode = enums.GameMode(details["mode"])
1933        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1934        is_private = details["isPrivate"]
1935        membership_type = enums.MembershipType(int(details["membershipType"]))
1936        return activity.PostActivity(
1937            net=self._net,
1938            hash=ref_id,
1939            membership_type=membership_type,
1940            instance_id=instance_id,
1941            mode=mode,
1942            modes=modes,
1943            is_private=is_private,
1944            occurred_at=period,
1945            starting_phase=int(payload["startingPhaseIndex"]),
1946            players=[
1947                self.deserialize_post_activity_player(player)
1948                for player in payload["entries"]
1949            ],
1950            teams=[
1951                self._deserialize_post_activity_team(team) for team in payload["teams"]
1952            ],
1953        )

Deserialize a JSON payload of a post activity information.

Parameters
Returns
def deserialize_aggregated_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.AggregatedActivity:
1991    def deserialize_aggregated_activity(
1992        self, payload: typedefs.JSONObject
1993    ) -> activity.AggregatedActivity:
1994        return activity.AggregatedActivity(
1995            hash=int(payload["activityHash"]),
1996            values=self._deserialize_aggregated_activity_values(payload["values"]),
1997        )

Deserialize a JSON payload of an aggregated activity.

Parameters
Returns
def deserialize_aggregated_activities( self, payload: dict[str, typing.Any]) -> aiobungie.Iterator[aiobungie.crates.activity.AggregatedActivity]:
1999    def deserialize_aggregated_activities(
2000        self, payload: typedefs.JSONObject
2001    ) -> iterators.Iterator[activity.AggregatedActivity]:
2002        return iterators.Iterator(
2003            [
2004                self.deserialize_aggregated_activity(activity)
2005                for activity in payload["activities"]
2006            ]
2007        )

Deserialize a JSON payload of an array of aggregated activities.

Parameters
Returns
def deserialize_linked_profiles( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.LinkedProfile:
2009    def deserialize_linked_profiles(
2010        self, payload: typedefs.JSONObject
2011    ) -> profile.LinkedProfile:
2012        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
2013        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2014        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2015
2016        if raw_profile := payload.get("profiles"):
2017            for pfile in raw_profile:
2018                profiles_vec.append(self.deserialize_destiny_membership(pfile))
2019
2020        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
2021            for raw_error_pfile in raw_profiles_with_errors:
2022                if error_pfile := raw_error_pfile.get("infoCard"):
2023                    error_profiles_vec.append(
2024                        self.deserialize_destiny_membership(error_pfile)
2025                    )
2026
2027        return profile.LinkedProfile(
2028            net=self._net,
2029            bungie=bungie_user,
2030            profiles=profiles_vec,
2031            profiles_with_errors=error_profiles_vec,
2032        )

Deserialize a JSON payload of Bungie.net hard linked profile information.

Parameters
Returns
def deserialize_clan_banners( self, payload: dict[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
2034    def deserialize_clan_banners(
2035        self, payload: typedefs.JSONObject
2036    ) -> collections.Sequence[clans.ClanBanner]:
2037        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
2038        if banners := payload.get("clanBannerDecals"):
2039            for k, v in banners.items():
2040                banner_obj = clans.ClanBanner(
2041                    id=int(k),
2042                    foreground=assets.Image(v["foregroundPath"]),
2043                    background=assets.Image(v["backgroundPath"]),
2044                )
2045                banners_seq.append(banner_obj)
2046        return banners_seq

Deserialize a JSON array of a clan banners information.

Parameters
Returns
def deserialize_public_milestone_content( self, payload: dict[str, typing.Any]) -> aiobungie.crates.milestones.MilestoneContent:
2048    def deserialize_public_milestone_content(
2049        self, payload: typedefs.JSONObject
2050    ) -> milestones.MilestoneContent:
2051        items_categoris: typedefs.NoneOr[milestones.MilestoneItems] = None
2052        if raw_categories := payload.get("itemCategories"):
2053            for item in raw_categories:
2054                title = undefined.UNDEFINED
2055                if raw_title := item.get("title"):
2056                    if raw_title != typedefs.Unknown:
2057                        title = raw_title
2058                if raw_hashes := item.get("itemHashes"):
2059                    hashes: collections.Sequence[int] = raw_hashes
2060
2061                items_categoris = milestones.MilestoneItems(title=title, hashes=hashes)
2062
2063        about = undefined.UNDEFINED
2064        if (raw_about := payload["about"]) != typedefs.Unknown:
2065            about = raw_about
2066
2067        status = undefined.UNDEFINED
2068        if (raw_status := payload["status"]) != typedefs.Unknown:
2069            status = raw_status
2070
2071        tips: typing.MutableSequence[undefined.UndefinedOr[str]] = []
2072        if raw_tips := payload.get("tips"):
2073            for raw_tip in raw_tips:
2074                if raw_tip == typedefs.Unknown:
2075                    raw_tip = undefined.UNDEFINED
2076                tips.append(raw_tip)
2077
2078        return milestones.MilestoneContent(
2079            about=about, status=status, tips=tips, items=items_categoris
2080        )

Deserialize a JSON payload of milestone content information.

Parameters
Returns
def deserialize_friend( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.friends.Friend:
2082    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2083        name = undefined.UNDEFINED
2084        if (raw_name := payload["bungieGlobalDisplayName"]) != typedefs.Unknown:
2085            name = raw_name
2086
2087        bungie_user: typedefs.NoneOr[user.BungieUser] = None
2088
2089        if raw_bungie_user := payload.get("bungieNetUser"):
2090            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2091
2092        return friends.Friend(
2093            net=self._net,
2094            id=int(payload["lastSeenAsMembershipId"]),
2095            name=name,
2096            code=payload.get("bungieGlobalDisplayNameCode"),
2097            relationship=enums.Relationship(payload["relationship"]),
2098            user=bungie_user,
2099            online_status=enums.Presence(payload["onlineStatus"]),
2100            online_title=payload["onlineTitle"],
2101            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2102        )

Deserialize a JSON payload of a Bungie friend information.

Parameters
Returns
def deserialize_friends( self, payload: dict[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
2104    def deserialize_friends(
2105        self, payload: typedefs.JSONObject
2106    ) -> collections.Sequence[friends.Friend]:
2107        mut_seq: typing.MutableSequence[friends.Friend] = []
2108        if raw_friends := payload.get("friends"):
2109            for friend in raw_friends:
2110                mut_seq.append(self.deserialize_friend(friend))
2111        return mut_seq

Deserialize a JSON sequence of Bungie friends information.

This is usually used to deserialize the incoming/outgoing friend requests.

Parameters
Returns
def deserialize_friend_requests( self, payload: dict[str, typing.Any]) -> aiobungie.crates.friends.FriendRequestView:
2113    def deserialize_friend_requests(
2114        self, payload: typedefs.JSONObject
2115    ) -> friends.FriendRequestView:
2116        incoming: typing.MutableSequence[friends.Friend] = []
2117        outgoing: typing.MutableSequence[friends.Friend] = []
2118
2119        if raw_incoming_requests := payload.get("incomingRequests"):
2120            for incoming_request in raw_incoming_requests:
2121                incoming.append(self.deserialize_friend(incoming_request))
2122
2123        if raw_outgoing_requests := payload.get("outgoingRequests"):
2124            for outgoing_request in raw_outgoing_requests:
2125                outgoing.append(self.deserialize_friend(outgoing_request))
2126
2127        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)

Deserialize a JSON sequence of Bungie friend requests information.

This is used for incoming/outgoing friend requests.

Parameters
Returns
def deserialize_fireteams( self, payload: dict[str, typing.Any]) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
2152    def deserialize_fireteams(
2153        self, payload: typedefs.JSONObject
2154    ) -> typedefs.NoneOr[collections.Sequence[fireteams.Fireteam]]:
2155        fireteams_: typing.MutableSequence[fireteams.Fireteam] = []
2156
2157        result: list[typedefs.JSONObject]
2158        if not (result := payload["results"]):
2159            return None
2160        for elem in result:
2161            fireteams_.append(
2162                self._set_fireteam_fields(
2163                    elem, total_results=int(payload["totalResults"])
2164                )
2165            )
2166        return fireteams_

Deserialize a JSON sequence of Bungie fireteams information.

Parameters
Returns
def deserialize_fireteam_destiny_users( self, payload: dict[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamUser:
2168    def deserialize_fireteam_destiny_users(
2169        self, payload: typedefs.JSONObject
2170    ) -> fireteams.FireteamUser:
2171        destiny_obj = self.deserialize_destiny_membership(payload)
2172        # We could helpers.just return a DestinyMembership object but this is
2173        # missing the fireteam display name and id fields.
2174        return fireteams.FireteamUser(
2175            net=self._net,
2176            id=destiny_obj.id,
2177            code=destiny_obj.code,
2178            icon=destiny_obj.icon,
2179            types=destiny_obj.types,
2180            type=destiny_obj.type,
2181            is_public=destiny_obj.is_public,
2182            crossave_override=destiny_obj.crossave_override,
2183            name=destiny_obj.name,
2184            last_seen_name=destiny_obj.last_seen_name,
2185            fireteam_display_name=payload["FireteamDisplayName"],
2186            fireteam_membership_id=enums.MembershipType(
2187                payload["FireteamMembershipType"]
2188            ),
2189        )

Deserialize a JSON payload of Bungie fireteam destiny users information.

Parameters
Returns
def deserialize_fireteam_members( self, payload: dict[str, typing.Any], *, alternatives: bool = False) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.FireteamMember]]:
2191    def deserialize_fireteam_members(
2192        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2193    ) -> typing.Optional[collections.Sequence[fireteams.FireteamMember]]:
2194        members_: list[fireteams.FireteamMember] = []
2195        if members := payload.get("Members" if not alternatives else "Alternates"):
2196            for member in members:
2197                bungie_fields = self.deserialize_partial_bungie_user(member)
2198                members_fields = fireteams.FireteamMember(
2199                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2200                    has_microphone=member["hasMicrophone"],
2201                    character_id=int(member["characterId"]),
2202                    date_joined=time.clean_date(member["dateJoined"]),
2203                    last_platform_invite_date=time.clean_date(
2204                        member["lastPlatformInviteAttemptDate"]
2205                    ),
2206                    last_platform_invite_result=int(
2207                        member["lastPlatformInviteAttemptResult"]
2208                    ),
2209                    net=self._net,
2210                    name=bungie_fields.name,
2211                    id=bungie_fields.id,
2212                    icon=bungie_fields.icon,
2213                    is_public=bungie_fields.is_public,
2214                    crossave_override=bungie_fields.crossave_override,
2215                    types=bungie_fields.types,
2216                    type=bungie_fields.type,
2217                )
2218                members_.append(members_fields)
2219        else:
2220            return None
2221        return members_

Deserialize a JSON sequence of Bungie fireteam members information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • alternatives (bool): If set to True, Then it will deserialize the alternatives data in the payload. If not the it will just deserialize the members data.
Returns
def deserialize_available_fireteams( self, data: dict[str, typing.Any], *, no_results: bool = False) -> Union[aiobungie.crates.fireteams.AvailableFireteam, collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]]:
2223    def deserialize_available_fireteams(
2224        self,
2225        data: typedefs.JSONObject,
2226        *,
2227        no_results: bool = False,
2228    ) -> typing.Union[
2229        fireteams.AvailableFireteam, collections.Sequence[fireteams.AvailableFireteam]
2230    ]:
2231        fireteams_: list[fireteams.AvailableFireteam] = []
2232
2233        # This needs to be used outside the results
2234        # JSON key.
2235        if no_results is True:
2236            payload = data
2237
2238        if result := payload.get("results"):
2239
2240            for fireteam in result:
2241                found_fireteams = self._set_fireteam_fields(fireteam["Summary"])
2242                fireteams_fields = fireteams.AvailableFireteam(
2243                    id=found_fireteams.id,
2244                    group_id=found_fireteams.group_id,
2245                    platform=found_fireteams.platform,
2246                    activity_type=found_fireteams.activity_type,
2247                    is_immediate=found_fireteams.is_immediate,
2248                    is_public=found_fireteams.is_public,
2249                    is_valid=found_fireteams.is_valid,
2250                    owner_id=found_fireteams.owner_id,
2251                    player_slot_count=found_fireteams.player_slot_count,
2252                    available_player_slots=found_fireteams.available_player_slots,
2253                    available_alternate_slots=found_fireteams.available_alternate_slots,
2254                    title=found_fireteams.title,
2255                    date_created=found_fireteams.date_created,
2256                    locale=found_fireteams.locale,
2257                    last_modified=found_fireteams.last_modified,
2258                    total_results=found_fireteams.total_results,
2259                    members=self.deserialize_fireteam_members(payload),
2260                    alternatives=self.deserialize_fireteam_members(
2261                        payload, alternatives=True
2262                    ),
2263                )
2264            fireteams_.append(fireteams_fields)
2265            if no_results:
2266                return fireteams_fields
2267        return fireteams_

Deserialize a JSON payload of a sequence of/fireteam information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • no_results (bool): Whether to deserialize the data from results in the payload or not.
Returns
  • typing.Union[aiobungie.crates.fireteams.AvailableFireteam, collections.Sequence[aiobungie.crates.fireteams.AvailableFireteam]] # noqa (E501): An available fireteam or a sequence of available fireteam.
def deserialize_fireteam_party( self, payload: dict[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamParty:
2269    def deserialize_fireteam_party(
2270        self, payload: typedefs.JSONObject
2271    ) -> fireteams.FireteamParty:
2272        last_destination_hash: typing.Optional[int] = None
2273        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2274            last_destination_hash = int(raw_dest_hash)
2275
2276        return fireteams.FireteamParty(
2277            members=[
2278                self._deserialize_fireteam_party_member(member)
2279                for member in payload["partyMembers"]
2280            ],
2281            activity=self._deserialize_fireteam_party_current_activity(
2282                payload["currentActivity"]
2283            ),
2284            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2285            last_destination_hash=last_destination_hash,
2286            tracking=payload["tracking"],
2287        )

Deserialize a JSON payload of profileTransitory component response.

Parameters
Returns
def deserialize_seasonal_artifact(self, payload: dict[str, typing.Any]) -> aiobungie.crates.season.Artifact:
2334    def deserialize_seasonal_artifact(
2335        self, payload: typedefs.JSONObject
2336    ) -> season.Artifact:
2337        if raw_artifact := payload.get("seasonalArtifact"):
2338            if points := raw_artifact.get("pointProgression"):
2339                points_prog = progressions.Progression(
2340                    hash=points["progressionHash"],
2341                    level=points["level"],
2342                    cap=points["levelCap"],
2343                    daily_limit=points["dailyLimit"],
2344                    weekly_limit=points["weeklyLimit"],
2345                    current_progress=points["currentProgress"],
2346                    daily_progress=points["dailyProgress"],
2347                    needed=points["progressToNextLevel"],
2348                    next_level=points["nextLevelAt"],
2349                )
2350
2351            if bonus := raw_artifact.get("powerBonusProgression"):
2352                power_bonus_prog = progressions.Progression(
2353                    hash=bonus["progressionHash"],
2354                    level=bonus["level"],
2355                    cap=bonus["levelCap"],
2356                    daily_limit=bonus["dailyLimit"],
2357                    weekly_limit=bonus["weeklyLimit"],
2358                    current_progress=bonus["currentProgress"],
2359                    daily_progress=bonus["dailyProgress"],
2360                    needed=bonus["progressToNextLevel"],
2361                    next_level=bonus["nextLevelAt"],
2362                )
2363            artifact = season.Artifact(
2364                net=self._net,
2365                hash=raw_artifact["artifactHash"],
2366                power_bonus=raw_artifact["powerBonus"],
2367                acquired_points=raw_artifact["pointsAcquired"],
2368                bonus=power_bonus_prog,
2369                points=points_prog,
2370            )
2371        return artifact

Deserialize a JSON payload of a Destiny 2 seasonal artifact information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_profile_progression( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.ProfileProgression:
2373    def deserialize_profile_progression(
2374        self, payload: typedefs.JSONObject
2375    ) -> profile.ProfileProgression:
2376        return profile.ProfileProgression(
2377            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2378            checklist={
2379                int(check_id): checklists
2380                for check_id, checklists in payload["data"]["checklists"].items()
2381            },
2382        )

Deserialize a JSON payload of a profile progression component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_instanced_item( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemInstance:
2384    def deserialize_instanced_item(
2385        self, payload: typedefs.JSONObject
2386    ) -> items.ItemInstance:
2387        damage_type_hash: typing.Optional[int] = None
2388        if raw_damagetype_hash := payload.get("damageTypeHash"):
2389            damage_type_hash = int(raw_damagetype_hash)
2390
2391        required_hashes: typing.Optional[collections.Collection[int]] = None
2392        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2393            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2394
2395        breaker_type: typing.Optional[items.ItemBreakerType] = None
2396        if raw_break_type := payload.get("breakerType"):
2397            breaker_type = items.ItemBreakerType(int(raw_break_type))
2398
2399        breaker_type_hash: typing.Optional[int] = None
2400        if raw_break_type_hash := payload.get("breakerTypeHash"):
2401            breaker_type_hash = int(raw_break_type_hash)
2402
2403        energy: typing.Optional[items.ItemEnergy] = None
2404        if raw_energy := payload.get("energy"):
2405            energy = self.deserialize_item_energy(raw_energy)
2406
2407        primary_stats = None
2408        if raw_primary_stats := payload.get("primaryStat"):
2409            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2410
2411        return items.ItemInstance(
2412            damage_type=enums.DamageType(int(payload["damageType"])),
2413            damage_type_hash=damage_type_hash,
2414            primary_stat=primary_stats,
2415            item_level=int(payload["itemLevel"]),
2416            quality=int(payload["quality"]),
2417            is_equipped=payload["isEquipped"],
2418            can_equip=payload["canEquip"],
2419            equip_required_level=int(payload["equipRequiredLevel"]),
2420            required_equip_unlock_hashes=required_hashes,
2421            cant_equip_reason=int(payload["cannotEquipReason"]),
2422            breaker_type=breaker_type,
2423            breaker_type_hash=breaker_type_hash,
2424            energy=energy,
2425        )

Deserialize a JSON object into an instanced item.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_item_energy( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemEnergy:
2427    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2428        energy_hash: typing.Optional[int] = None
2429        if raw_energy_hash := payload.get("energyTypeHash"):
2430            energy_hash = int(raw_energy_hash)
2431
2432        return items.ItemEnergy(
2433            hash=energy_hash,
2434            type=items.ItemEnergyType(int(payload["energyType"])),
2435            capacity=int(payload["energyCapacity"]),
2436            used_energy=int(payload["energyUsed"]),
2437            unused_energy=int(payload["energyUnused"]),
2438        )
def deserialize_item_perk(self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemPerk:
2440    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2441        perk_hash: typing.Optional[int] = None
2442        if raw_perk_hash := payload.get("perkHash"):
2443            perk_hash = int(raw_perk_hash)
2444
2445        return items.ItemPerk(
2446            hash=perk_hash,
2447            icon=assets.Image(payload["iconPath"]),
2448            is_active=payload["isActive"],
2449            is_visible=payload["visible"],
2450        )
def deserialize_item_socket( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemSocket:
2452    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2453        plug_hash: typing.Optional[int] = None
2454        if raw_plug_hash := payload.get("plugHash"):
2455            plug_hash = int(raw_plug_hash)
2456
2457        enable_fail_indexes: typing.Optional[list[int]] = None
2458        if raw_indexes := payload.get("enableFailIndexes"):
2459            enable_fail_indexes = [int(index) for index in raw_indexes]
2460
2461        return items.ItemSocket(
2462            plug_hash=plug_hash,
2463            is_enabled=payload["isEnabled"],
2464            enable_fail_indexes=enable_fail_indexes,
2465            is_visible=payload.get("visible"),
2466        )
def deserialize_item_stats_view( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemStatsView:
2468    def deserialize_item_stats_view(
2469        self, payload: typedefs.JSONObject
2470    ) -> items.ItemStatsView:
2471        return items.ItemStatsView(
2472            stat_hash=payload.get("statHash"), value=payload.get("value")
2473        )
def deserialize_plug_item_state( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.PlugItemState:
2475    def deserialize_plug_item_state(
2476        self, payload: typedefs.JSONObject
2477    ) -> items.PlugItemState:
2478        item_hash: typing.Optional[int] = None
2479        if raw_item_hash := payload.get("plugItemHash"):
2480            item_hash = int(raw_item_hash)
2481
2482        insert_fail_indexes: typedefs.NoneOr[list[int]] = None
2483        if raw_fail_indexes := payload.get("insertFailIndexes"):
2484            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2485
2486        enable_fail_indexes: typedefs.NoneOr[list[int]] = None
2487        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2488            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2489
2490        return items.PlugItemState(
2491            item_hash=item_hash,
2492            insert_fail_indexes=insert_fail_indexes,
2493            enable_fail_indexes=enable_fail_indexes,
2494            is_enabled=payload["enabled"],
2495            can_insert=payload["canInsert"],
2496        )
@typing.final
class FireteamActivity(builtins.int, aiobungie.Enum):
 67@typing.final
 68class FireteamActivity(int, enums.Enum):
 69    """An enum for the fireteam activities."""
 70
 71    ALL = 0
 72    CRUCIBLE = 2
 73    TRIALS_OF_OSIRIS = 3
 74    NIGHTFALL = 4
 75    ANY = 5
 76    GAMBIT = 6
 77    BLIND_WELL = 7
 78    NIGHTMARE_HUNTS = 12
 79    ALTARS_OF_SORROWS = 14
 80    DUNGEON = 15
 81    RAID_LW = 20
 82    RAID_GOS = 21
 83    RAID_DSC = 22
 84    EXO_CHALLENGE = 23
 85    S12_WRATHBORN = 24
 86    EMPIRE_HUNTS = 25
 87    S13_BATTLEGROUNDS = 26
 88    EXOTIC_QUEST = 27
 89    RAID_VOG = 28
 90    S14_EXPUNGE = 30
 91    S15_ASTRAL_ALIGNMENT = 31
 92    S15_SHATTERED_RELAM = 32
 93    SHATTERED_THRONE = 33
 94    PROPHECY = 34
 95    PIT_OF_HERESY = 35
 96    DOE = 36
 97    """Dares of Eternity."""
 98    DUNGEON_GOA = 37
 99    """Grasp of Avarice."""
100    VOW_OF_THE_DISCPILE = 38
101    CAMPAIGN = 39
102    WELLSPRING = 40
103    S16_BATTLEGROUNDS = 41
104    S17_NIGHTMARE_CONTAINMENT = 44
105    S17_SEVER = 45

An enum for the fireteam activities.

CRUCIBLE = <FireteamActivity.CRUCIBLE: 2>
TRIALS_OF_OSIRIS = <FireteamActivity.TRIALS_OF_OSIRIS: 3>
NIGHTFALL = <FireteamActivity.NIGHTFALL: 4>
GAMBIT = <FireteamActivity.GAMBIT: 6>
BLIND_WELL = <FireteamActivity.BLIND_WELL: 7>
NIGHTMARE_HUNTS = <FireteamActivity.NIGHTMARE_HUNTS: 12>
ALTARS_OF_SORROWS = <FireteamActivity.ALTARS_OF_SORROWS: 14>
DUNGEON = <FireteamActivity.DUNGEON: 15>
RAID_LW = <FireteamActivity.RAID_LW: 20>
RAID_GOS = <FireteamActivity.RAID_GOS: 21>
RAID_DSC = <FireteamActivity.RAID_DSC: 22>
EXO_CHALLENGE = <FireteamActivity.EXO_CHALLENGE: 23>
S12_WRATHBORN = <FireteamActivity.S12_WRATHBORN: 24>
EMPIRE_HUNTS = <FireteamActivity.EMPIRE_HUNTS: 25>
S13_BATTLEGROUNDS = <FireteamActivity.S13_BATTLEGROUNDS: 26>
EXOTIC_QUEST = <FireteamActivity.EXOTIC_QUEST: 27>
RAID_VOG = <FireteamActivity.RAID_VOG: 28>
S14_EXPUNGE = <FireteamActivity.S14_EXPUNGE: 30>
S15_ASTRAL_ALIGNMENT = <FireteamActivity.S15_ASTRAL_ALIGNMENT: 31>
S15_SHATTERED_RELAM = <FireteamActivity.S15_SHATTERED_RELAM: 32>
SHATTERED_THRONE = <FireteamActivity.SHATTERED_THRONE: 33>
PROPHECY = <FireteamActivity.PROPHECY: 34>
PIT_OF_HERESY = <FireteamActivity.PIT_OF_HERESY: 35>
DOE = <FireteamActivity.DOE: 36>

Dares of Eternity.

DUNGEON_GOA = <FireteamActivity.DUNGEON_GOA: 37>

Grasp of Avarice.

VOW_OF_THE_DISCPILE = <FireteamActivity.VOW_OF_THE_DISCPILE: 38>
CAMPAIGN = <FireteamActivity.CAMPAIGN: 39>
WELLSPRING = <FireteamActivity.WELLSPRING: 40>
S16_BATTLEGROUNDS = <FireteamActivity.S16_BATTLEGROUNDS: 41>
S17_NIGHTMARE_CONTAINMENT = <FireteamActivity.S17_NIGHTMARE_CONTAINMENT: 44>
S17_SEVER = <FireteamActivity.S17_SEVER: 45>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamDate(builtins.int, aiobungie.Enum):
131@typing.final
132class FireteamDate(int, enums.Enum):
133    """An enum for fireteam date ranges."""
134
135    ALL = 0
136    NOW = 1
137    TODAY = 2
138    TWO_DAYS = 3
139    THIS_WEEK = 4

An enum for fireteam date ranges.

ALL = <FireteamDate.ALL: 0>
NOW = <FireteamDate.NOW: 1>
TODAY = <FireteamDate.TODAY: 2>
TWO_DAYS = <FireteamDate.TWO_DAYS: 3>
THIS_WEEK = <FireteamDate.THIS_WEEK: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamLanguage(builtins.str, aiobungie.Enum):
108@typing.final
109class FireteamLanguage(str, enums.Enum):
110    """An enum for fireteams languages filters."""
111
112    ALL = ""
113    ENGLISH = "en"
114    FRENCH = "fr"
115    ESPANOL = "es"
116    DEUTSCH = "de"
117    ITALIAN = "it"
118    JAPANESE = "ja"
119    PORTUGUESE = "pt-br"
120    RUSSIAN = "ru"
121    POLISH = "pl"
122    KOREAN = "ko"
123    # ? China
124    ZH_CHT = "zh-cht"
125    ZH_CHS = "zh-chs"
126
127    def __str__(self) -> str:
128        return str(self.value)

An enum for fireteams languages filters.

ENGLISH = <FireteamLanguage.ENGLISH: en>
FRENCH = <FireteamLanguage.FRENCH: fr>
ESPANOL = <FireteamLanguage.ESPANOL: es>
DEUTSCH = <FireteamLanguage.DEUTSCH: de>
ITALIAN = <FireteamLanguage.ITALIAN: it>
JAPANESE = <FireteamLanguage.JAPANESE: ja>
PORTUGUESE = <FireteamLanguage.PORTUGUESE: pt-br>
RUSSIAN = <FireteamLanguage.RUSSIAN: ru>
POLISH = <FireteamLanguage.POLISH: pl>
KOREAN = <FireteamLanguage.KOREAN: ko>
ZH_CHT = <FireteamLanguage.ZH_CHT: zh-cht>
ZH_CHS = <FireteamLanguage.ZH_CHS: zh-chs>
Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@typing.final
class FireteamPlatform(builtins.int, aiobungie.Enum):
54@typing.final
55class FireteamPlatform(int, enums.Enum):
56    """An enum for fireteam related to bungie fireteams.
57    This is different from the normal `aiobungie.MembershipType`.
58    """
59
60    ANY = 0
61    PSN_NETWORK = 1
62    XBOX_LIVE = 2
63    STEAM = 4
64    STADIA = 5

An enum for fireteam related to bungie fireteams. This is different from the normal aiobungie.MembershipType.

PSN_NETWORK = <FireteamPlatform.PSN_NETWORK: 1>
XBOX_LIVE = <FireteamPlatform.XBOX_LIVE: 2>
STEAM = <FireteamPlatform.STEAM: 4>
STADIA = <FireteamPlatform.STADIA: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Flag(enum.Flag):
 97class Flag(__enum.Flag):
 98    """Builtin Python enum flag with extra handlings."""
 99
100    # Needs to type this here for mypy
101    _value_: int
102
103    @property
104    def name(self) -> str:  # type: ignore[override]
105        if self._name_ is None:
106            self._name_ = f"UNKNOWN {self._value_}"
107
108        return self._name_
109
110    @property
111    def value(self) -> int:  # type: ignore[override]
112        return self._value_
113
114    def __str__(self) -> str:
115        return self.name
116
117    def __repr__(self) -> str:
118        return f"<{type(self).__name__}.{self.name}: {self._value_!s}>"
119
120    def __int__(self) -> int:
121        if isinstance(self.value, _ITERABLE):
122            raise TypeError(
123                f"Can't overload {self.value} in {type(self).__name__}, Please use `.value` attribute.",
124            )
125        return int(self.value)
126
127    def __or__(self, other: typing.Union[Flag, int]) -> Flag:
128        return self.__class__(self._value_ | int(other))
129
130    def __xor__(self, other: typing.Union[Flag, int]) -> Flag:
131        return self.__class__(self._value_ ^ int(other))
132
133    def __and__(self, other: typing.Union[Flag, int]) -> Flag:
134        return self.__class__(other & int(other))
135
136    def __invert__(self) -> Flag:
137        return self.__class__(~self._value_)
138
139    def __contains__(self, other: typing.Union[Flag, int]) -> bool:
140        return self.value & int(other) == int(other)

Builtin Python enum flag with extra handlings.

name: str

The name of the Enum member.

value: int

The value of the Enum member.

@attrs.define(auto_exc=True)
class Forbidden(aiobungie.HTTPException):
137@attrs.define(auto_exc=True)
138class Forbidden(HTTPException):
139    """Exception that's raised for when status code 403 occurs."""
140
141    http_status: http.HTTPStatus = attrs.field(
142        default=http.HTTPStatus.FORBIDDEN, init=False
143    )

Exception that's raised for when status code 403 occurs.

Forbidden( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Forbidden.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class GameMode(builtins.int, aiobungie.Enum):
269@typing.final
270class GameMode(int, Enum):
271    """An Enum for all available gamemodes in Destiny 2."""
272
273    NONE = 0
274    STORY = 2
275    STRIKE = 3
276    RAID = 4
277    ALLPVP = 5
278    PATROL = 6
279    ALLPVE = 7
280    RESERVED9 = 9
281    CONTROL = 10
282    RESERVED11 = 11
283    CLASH = 12
284    RESERVED13 = 13
285    CRIMSONDOUBLES = 15
286    NIGHTFALL = 16
287    HEROICNIGHTFALL = 17
288    ALLSTRIKES = 18
289    IRONBANNER = 19
290    RESERVED20 = 20
291    RESERVED21 = 21
292    RESERVED22 = 22
293    RESERVED24 = 24
294    ALLMAYHEM = 25
295    RESERVED26 = 26
296    RESERVED27 = 27
297    RESERVED28 = 28
298    RESERVED29 = 29
299    RESERVED30 = 30
300    SUPREMACY = 31
301    PRIVATEMATCHESALL = 32
302    SURVIVAL = 37
303    COUNTDOWN = 38
304    TRIALSOFTHENINE = 39
305    SOCIAL = 40
306    TRIALSCOUNTDOWN = 41
307    TRIALSSURVIVAL = 42
308    IRONBANNERCONTROL = 43
309    IRONBANNERCLASH = 44
310    IRONBANNERSUPREMACY = 45
311    SCOREDNIGHTFALL = 46
312    SCOREDHEROICNIGHTFALL = 47
313    RUMBLE = 48
314    ALLDOUBLES = 49
315    DOUBLES = 50
316    PRIVATEMATCHESCLASH = 51
317    PRIVATEMATCHESCONTROL = 52
318    PRIVATEMATCHESSUPREMACY = 53
319    PRIVATEMATCHESCOUNTDOWN = 54
320    PRIVATEMATCHESSURVIVAL = 55
321    PRIVATEMATCHESMAYHEM = 56
322    PRIVATEMATCHESRUMBLE = 57
323    HEROICADVENTURE = 58
324    SHOWDOWN = 59
325    LOCKDOWN = 60
326    SCORCHED = 61
327    SCORCHEDTEAM = 62
328    GAMBIT = 63
329    ALLPVECOMPETITIVE = 64
330    BREAKTHROUGH = 65
331    BLACKARMORYRUN = 66
332    SALVAGE = 67
333    IRONBANNERSALVAGE = 68
334    PVPCOMPETITIVE = 69
335    PVPQUICKPLAY = 70
336    CLASHQUICKPLAY = 71
337    CLASHCOMPETITIVE = 72
338    CONTROLQUICKPLAY = 73
339    CONTROLCOMPETITIVE = 74
340    GAMBITPRIME = 75
341    RECKONING = 76
342    MENAGERIE = 77
343    VEXOFFENSIVE = 78
344    NIGHTMAREHUNT = 79
345    ELIMINATION = 80
346    MOMENTUM = 81
347    DUNGEON = 82
348    SUNDIAL = 83
349    TRIALS_OF_OSIRIS = 84
350    DARES = 85
351    OFFENSIVE = 86
352    LOSTSECTOR = 87
353    RIFT = 88
354    ZONECONTROL = 89
355    IRONBANNERRIFT = 90

An Enum for all available gamemodes in Destiny 2.

NONE = <GameMode.NONE: 0>
STORY = <GameMode.STORY: 2>
STRIKE = <GameMode.STRIKE: 3>
RAID = <GameMode.RAID: 4>
ALLPVP = <GameMode.ALLPVP: 5>
PATROL = <GameMode.PATROL: 6>
ALLPVE = <GameMode.ALLPVE: 7>
RESERVED9 = <GameMode.RESERVED9: 9>
CONTROL = <GameMode.CONTROL: 10>
RESERVED11 = <GameMode.RESERVED11: 11>
CLASH = <GameMode.CLASH: 12>
RESERVED13 = <GameMode.RESERVED13: 13>
CRIMSONDOUBLES = <GameMode.CRIMSONDOUBLES: 15>
NIGHTFALL = <GameMode.NIGHTFALL: 16>
HEROICNIGHTFALL = <GameMode.HEROICNIGHTFALL: 17>
ALLSTRIKES = <GameMode.ALLSTRIKES: 18>
IRONBANNER = <GameMode.IRONBANNER: 19>
RESERVED20 = <GameMode.RESERVED20: 20>
RESERVED21 = <GameMode.RESERVED21: 21>
RESERVED22 = <GameMode.RESERVED22: 22>
RESERVED24 = <GameMode.RESERVED24: 24>
ALLMAYHEM = <GameMode.ALLMAYHEM: 25>
RESERVED26 = <GameMode.RESERVED26: 26>
RESERVED27 = <GameMode.RESERVED27: 27>
RESERVED28 = <GameMode.RESERVED28: 28>
RESERVED29 = <GameMode.RESERVED29: 29>
RESERVED30 = <GameMode.RESERVED30: 30>
SUPREMACY = <GameMode.SUPREMACY: 31>
PRIVATEMATCHESALL = <GameMode.PRIVATEMATCHESALL: 32>
SURVIVAL = <GameMode.SURVIVAL: 37>
COUNTDOWN = <GameMode.COUNTDOWN: 38>
TRIALSOFTHENINE = <GameMode.TRIALSOFTHENINE: 39>
SOCIAL = <GameMode.SOCIAL: 40>
TRIALSCOUNTDOWN = <GameMode.TRIALSCOUNTDOWN: 41>
TRIALSSURVIVAL = <GameMode.TRIALSSURVIVAL: 42>
IRONBANNERCONTROL = <GameMode.IRONBANNERCONTROL: 43>
IRONBANNERCLASH = <GameMode.IRONBANNERCLASH: 44>
IRONBANNERSUPREMACY = <GameMode.IRONBANNERSUPREMACY: 45>
SCOREDNIGHTFALL = <GameMode.SCOREDNIGHTFALL: 46>
SCOREDHEROICNIGHTFALL = <GameMode.SCOREDHEROICNIGHTFALL: 47>
RUMBLE = <GameMode.RUMBLE: 48>
ALLDOUBLES = <GameMode.ALLDOUBLES: 49>
DOUBLES = <GameMode.DOUBLES: 50>
PRIVATEMATCHESCLASH = <GameMode.PRIVATEMATCHESCLASH: 51>
PRIVATEMATCHESCONTROL = <GameMode.PRIVATEMATCHESCONTROL: 52>
PRIVATEMATCHESSUPREMACY = <GameMode.PRIVATEMATCHESSUPREMACY: 53>
PRIVATEMATCHESCOUNTDOWN = <GameMode.PRIVATEMATCHESCOUNTDOWN: 54>
PRIVATEMATCHESSURVIVAL = <GameMode.PRIVATEMATCHESSURVIVAL: 55>
PRIVATEMATCHESMAYHEM = <GameMode.PRIVATEMATCHESMAYHEM: 56>
PRIVATEMATCHESRUMBLE = <GameMode.PRIVATEMATCHESRUMBLE: 57>
HEROICADVENTURE = <GameMode.HEROICADVENTURE: 58>
SHOWDOWN = <GameMode.SHOWDOWN: 59>
LOCKDOWN = <GameMode.LOCKDOWN: 60>
SCORCHED = <GameMode.SCORCHED: 61>
SCORCHEDTEAM = <GameMode.SCORCHEDTEAM: 62>
GAMBIT = <GameMode.GAMBIT: 63>
ALLPVECOMPETITIVE = <GameMode.ALLPVECOMPETITIVE: 64>
BREAKTHROUGH = <GameMode.BREAKTHROUGH: 65>
BLACKARMORYRUN = <GameMode.BLACKARMORYRUN: 66>
SALVAGE = <GameMode.SALVAGE: 67>
IRONBANNERSALVAGE = <GameMode.IRONBANNERSALVAGE: 68>
PVPCOMPETITIVE = <GameMode.PVPCOMPETITIVE: 69>
PVPQUICKPLAY = <GameMode.PVPQUICKPLAY: 70>
CLASHQUICKPLAY = <GameMode.CLASHQUICKPLAY: 71>
CLASHCOMPETITIVE = <GameMode.CLASHCOMPETITIVE: 72>
CONTROLQUICKPLAY = <GameMode.CONTROLQUICKPLAY: 73>
CONTROLCOMPETITIVE = <GameMode.CONTROLCOMPETITIVE: 74>
GAMBITPRIME = <GameMode.GAMBITPRIME: 75>
RECKONING = <GameMode.RECKONING: 76>
MENAGERIE = <GameMode.MENAGERIE: 77>
VEXOFFENSIVE = <GameMode.VEXOFFENSIVE: 78>
NIGHTMAREHUNT = <GameMode.NIGHTMAREHUNT: 79>
ELIMINATION = <GameMode.ELIMINATION: 80>
MOMENTUM = <GameMode.MOMENTUM: 81>
DUNGEON = <GameMode.DUNGEON: 82>
SUNDIAL = <GameMode.SUNDIAL: 83>
TRIALS_OF_OSIRIS = <GameMode.TRIALS_OF_OSIRIS: 84>
DARES = <GameMode.DARES: 85>
OFFENSIVE = <GameMode.OFFENSIVE: 86>
LOSTSECTOR = <GameMode.LOSTSECTOR: 87>
RIFT = <GameMode.RIFT: 88>
ZONECONTROL = <GameMode.ZONECONTROL: 89>
IRONBANNERRIFT = <GameMode.IRONBANNERRIFT: 90>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GatingScope(builtins.int, aiobungie.Enum):
58@typing.final
59class GatingScope(int, enums.Enum):
60    """An enum represents restrictive type of gating that is being performed by an entity.
61
62    This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity
63    applies to everyone equally, or to their specific Profile or Character states.
64    """
65
66    NONE = 0
67    GLOBAL = 1
68    CLAN = 2
69    PROFILE = 3
70    CHARACTER = 4
71    ITEM = 5
72    ASSUMED_WORST_CASE = 6

An enum represents restrictive type of gating that is being performed by an entity.

This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity applies to everyone equally, or to their specific Profile or Character states.

NONE = <GatingScope.NONE: 0>
GLOBAL = <GatingScope.GLOBAL: 1>
CLAN = <GatingScope.CLAN: 2>
PROFILE = <GatingScope.PROFILE: 3>
CHARACTER = <GatingScope.CHARACTER: 4>
ITEM = <GatingScope.ITEM: 5>
ASSUMED_WORST_CASE = <GatingScope.ASSUMED_WORST_CASE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Gender(builtins.int, aiobungie.Enum):
484@typing.final
485class Gender(int, Enum):
486    """An Enum for Destiny Genders."""
487
488    MALE = 0
489    FEMALE = 1
490    UNKNOWN = 2

An Enum for Destiny Genders.

MALE = <Gender.MALE: 0>
FEMALE = <Gender.FEMALE: 1>
UNKNOWN = <Gender.UNKNOWN: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GroupType(builtins.int, aiobungie.Enum):
653@typing.final
654class GroupType(int, Enum):
655    """An enums for the known bungie group types."""
656
657    GENERAL = 0
658    CLAN = 1

An enums for the known bungie group types.

GENERAL = <GroupType.GENERAL: 0>
CLAN = <GroupType.CLAN: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class HTTPError(aiobungie.AiobungieError):
79@attrs.define(auto_exc=True)
80class HTTPError(AiobungieError):
81    """Base HTTP request errors exception."""
82
83    message: str
84    """The error message."""
85
86    http_status: http.HTTPStatus
87    """The response status."""

Base HTTP request errors exception.

HTTPError(message: str, http_status: http.HTTPStatus)
2def __init__(self, message, http_status):
3    self.message = message
4    self.http_status = http_status
5    BaseException.__init__(self, self.message,self.http_status)

Method generated by attrs for class HTTPError.

message: str

The error message.

http_status: http.HTTPStatus

The response status.

Inherited Members
builtins.BaseException
with_traceback
@attrs.define(auto_exc=True, kw_only=True)
class HTTPException(aiobungie.HTTPError):
 90@attrs.define(auto_exc=True, kw_only=True)
 91class HTTPException(HTTPError):
 92    """An in-depth HTTP exception that's raised with more information."""
 93
 94    error_code: int
 95    """The returned Bungie error status code."""
 96
 97    http_status: http.HTTPStatus
 98    """The request response http status."""
 99
100    throttle_seconds: int
101    """The Bungie response throttle seconds."""
102
103    url: typing.Optional[typedefs.StrOrURL]
104    """The URL/endpoint caused this error."""
105
106    body: typing.Any
107    """The response body."""
108
109    headers: multidict.CIMultiDictProxy[str]
110    """The response headers."""
111
112    message: str
113    """A Bungie human readable message describes the cause of the error."""
114
115    error_status: str
116    """A Bungie short error status describes the cause of the error."""
117
118    message_data: dict[str, str]
119    """A dict of string key, value that includes each cause of the error
120    to a message describes information about that error.
121    """
122
123    def __str__(self) -> str:
124        if self.message:
125            message_body = self.message
126
127        if self.error_status:
128            error_status_body = self.error_status
129
130        return (
131            f"{self.http_status.name.replace('_', '').title()} {self.http_status.value}: "
132            f"Error status: {error_status_body}, Error message: {message_body} from {self.url} "
133            f"{str(self.body)}"
134        )

An in-depth HTTP exception that's raised with more information.

HTTPException( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class HTTPException.

error_code: int

The returned Bungie error status code.

http_status: http.HTTPStatus

The request response http status.

throttle_seconds: int

The Bungie response throttle seconds.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

message: str

A Bungie human readable message describes the cause of the error.

error_status: str

A Bungie short error status describes the cause of the error.

message_data: dict[str, str]

A dict of string key, value that includes each cause of the error to a message describes information about that error.

Inherited Members
builtins.BaseException
with_traceback
class Image:
 72class Image:
 73    """Representation of an image/avatar/picture at Bungie.
 74
 75    Example
 76    -------
 77    ```py
 78    from aiobungie import Image
 79    img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
 80    print(img)
 81    # https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg
 82
 83    # Stream the image.
 84    async for chunk in img:
 85        # Byte chunks of the image.
 86        print(chunk)
 87
 88    # Save the image to a file.
 89    await img.save("file_name", "/my/path/to/save/to", "jpeg")
 90    ```
 91
 92    Parameters
 93    ----------
 94    path : `str | None`
 95        The path to the image. If `None`, the default missing image path will be used.
 96    """
 97
 98    __slots__ = ("_path",)
 99
100    def __init__(self, path: typing.Optional[str] = None) -> None:
101        self._path = path
102
103    @property
104    def is_missing(self) -> bool:
105        return not self._path
106
107    @property
108    def url(self) -> str:
109        """The URL to the image."""
110        return self.create_url()
111
112    @staticmethod
113    def missing_path() -> str:
114        """Returns the path to the missing Bungie image."""
115        return "img/misc/missing_icon_d2.png"
116
117    def create_url(self) -> str:
118        """Creates a full URL to the image path.
119
120        Returns
121        -------
122        str
123            The URL to the image.
124        """
125        return f"{url.BASE}/{self._path if self._path else self.missing_path()}"
126
127    async def save(
128        self,
129        file_name: str,
130        path: typing.Union[pathlib.Path, str],
131        /,
132        mime_type: typing.Optional[typing.Union[MimeType, str]] = None,
133    ) -> None:
134        """Saves the image to a file.
135
136        Parameters
137        ----------
138        file_name : `str`
139            A name for the file to save the image to.
140        path : `pathlib.Path | str`
141            A path tp save the image to.
142
143        Other Parameters
144        ----------------
145        mime_type : `MimeType | str`
146            Optional MIME type of the image.
147
148        Raises
149        ------
150        `FileNotFoundError`
151            If the path provided does not exist.
152        `RuntimeError`
153            If the image could not be saved.
154        `PermissionError`
155            If the path provided is not writable or does not have write permissions.
156        """
157        if isinstance(path, pathlib.Path) and not path.exists():
158            raise FileNotFoundError(f"File does not exist: {path!r}")
159
160        if self.is_missing:
161            return
162
163        mimetype = mime_type or MimeType.PNG
164        path = pathlib.Path(path)
165
166        loop = helpers.get_or_make_loop()
167        pool = concurrent.futures.ThreadPoolExecutor()
168
169        try:
170            with pool:
171                await loop.run_in_executor(
172                    pool, _write, path, file_name, mimetype, await self.read()
173                )
174                _LOGGER.info("Saved image to %s", file_name)
175
176        except asyncio.CancelledError:
177            pass
178
179        except Exception as err:
180            raise RuntimeError("Encountered an error while saving image.") from err
181
182    async def read(self) -> bytes:
183        """Read this image bytes.
184
185        Returns
186        -------
187        `bytes`
188            The bytes of this image.
189        """
190        client_session = aiohttp.ClientSession()
191
192        try:
193            await client_session.__aenter__()
194            response = await client_session.get(self.create_url())
195
196            if 300 >= response.status >= 200:
197                reader = await response.read()
198
199        except Exception as exc:
200            raise RuntimeError(f"Failed to read image: {exc}") from None
201        finally:
202            await client_session.__aexit__(None, None, None)
203        return reader
204
205    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
206        """Iterates over the image bytes lazily.
207
208        Example
209        -------
210        import aiobungie
211
212        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
213        async for chunk in resource.iter():
214            print(chunk)
215
216        Returns
217        -------
218        `collections.AsyncGenerator[bytes, None]`
219            An async generator of the image bytes.
220        """
221
222        async for chunk in self:
223            yield chunk
224
225    def __repr__(self) -> str:
226        return f"Image(url={self.create_url()})"
227
228    def __str__(self) -> str:
229        return self.create_url()
230
231    def __aiter__(self) -> Image:
232        return self
233
234    async def __anext__(self) -> bytes:
235        return await self.read()
236
237    def __await__(self) -> collections.Generator[None, None, bytes]:
238        return self.__anext__().__await__()

Representation of an image/avatar/picture at Bungie.

Example
from aiobungie import Image
img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
print(img)
# https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg

# Stream the image.
async for chunk in img:
    # Byte chunks of the image.
    print(chunk)

# Save the image to a file.
await img.save("file_name", "/my/path/to/save/to", "jpeg")
Parameters
  • path (str | None): The path to the image. If None, the default missing image path will be used.
Image(path: Optional[str] = None)
100    def __init__(self, path: typing.Optional[str] = None) -> None:
101        self._path = path
url: str

The URL to the image.

@staticmethod
def missing_path() -> str:
112    @staticmethod
113    def missing_path() -> str:
114        """Returns the path to the missing Bungie image."""
115        return "img/misc/missing_icon_d2.png"

Returns the path to the missing Bungie image.

def create_url(self) -> str:
117    def create_url(self) -> str:
118        """Creates a full URL to the image path.
119
120        Returns
121        -------
122        str
123            The URL to the image.
124        """
125        return f"{url.BASE}/{self._path if self._path else self.missing_path()}"

Creates a full URL to the image path.

Returns
  • str: The URL to the image.
async def save( self, file_name: str, path: Union[pathlib.Path, str], /, mime_type: Union[aiobungie.internal.assets.MimeType, str, NoneType] = None) -> None:
127    async def save(
128        self,
129        file_name: str,
130        path: typing.Union[pathlib.Path, str],
131        /,
132        mime_type: typing.Optional[typing.Union[MimeType, str]] = None,
133    ) -> None:
134        """Saves the image to a file.
135
136        Parameters
137        ----------
138        file_name : `str`
139            A name for the file to save the image to.
140        path : `pathlib.Path | str`
141            A path tp save the image to.
142
143        Other Parameters
144        ----------------
145        mime_type : `MimeType | str`
146            Optional MIME type of the image.
147
148        Raises
149        ------
150        `FileNotFoundError`
151            If the path provided does not exist.
152        `RuntimeError`
153            If the image could not be saved.
154        `PermissionError`
155            If the path provided is not writable or does not have write permissions.
156        """
157        if isinstance(path, pathlib.Path) and not path.exists():
158            raise FileNotFoundError(f"File does not exist: {path!r}")
159
160        if self.is_missing:
161            return
162
163        mimetype = mime_type or MimeType.PNG
164        path = pathlib.Path(path)
165
166        loop = helpers.get_or_make_loop()
167        pool = concurrent.futures.ThreadPoolExecutor()
168
169        try:
170            with pool:
171                await loop.run_in_executor(
172                    pool, _write, path, file_name, mimetype, await self.read()
173                )
174                _LOGGER.info("Saved image to %s", file_name)
175
176        except asyncio.CancelledError:
177            pass
178
179        except Exception as err:
180            raise RuntimeError("Encountered an error while saving image.") from err

Saves the image to a file.

Parameters
  • file_name (str): A name for the file to save the image to.
  • path (pathlib.Path | str): A path tp save the image to.
Other Parameters
  • mime_type (MimeType | str): Optional MIME type of the image.
Raises
  • FileNotFoundError: If the path provided does not exist.
  • RuntimeError: If the image could not be saved.
  • PermissionError: If the path provided is not writable or does not have write permissions.
async def read(self) -> bytes:
182    async def read(self) -> bytes:
183        """Read this image bytes.
184
185        Returns
186        -------
187        `bytes`
188            The bytes of this image.
189        """
190        client_session = aiohttp.ClientSession()
191
192        try:
193            await client_session.__aenter__()
194            response = await client_session.get(self.create_url())
195
196            if 300 >= response.status >= 200:
197                reader = await response.read()
198
199        except Exception as exc:
200            raise RuntimeError(f"Failed to read image: {exc}") from None
201        finally:
202            await client_session.__aexit__(None, None, None)
203        return reader

Read this image bytes.

Returns
  • bytes: The bytes of this image.
async def iter(self) -> collections.abc.AsyncGenerator[bytes, None]:
205    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
206        """Iterates over the image bytes lazily.
207
208        Example
209        -------
210        import aiobungie
211
212        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
213        async for chunk in resource.iter():
214            print(chunk)
215
216        Returns
217        -------
218        `collections.AsyncGenerator[bytes, None]`
219            An async generator of the image bytes.
220        """
221
222        async for chunk in self:
223            yield chunk

Iterates over the image bytes lazily.

Example

import aiobungie

resource = aiobungie.Image("img/misc/missing_icon_d2.png") async for chunk in resource.iter(): print(chunk)

Returns
  • collections.AsyncGenerator[bytes, None]: An async generator of the image bytes.
@attrs.define(auto_exc=True)
class InternalServerError(aiobungie.HTTPException):
243@attrs.define(auto_exc=True)
244class InternalServerError(HTTPException):
245    """Raised for 5xx internal server errors."""

Raised for 5xx internal server errors.

InternalServerError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class InternalServerError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class ItemBindStatus(builtins.int, aiobungie.Enum):
719@typing.final
720class ItemBindStatus(int, Enum):
721    """An enum for Destiny 2 items bind status."""
722
723    NOT_BOUND = 0
724    BOUND_TO_CHARACTER = 1
725    BOUND_TO_ACCOUNT = 2
726    BOUNT_TO_GUILD = 3

An enum for Destiny 2 items bind status.

NOT_BOUND = <ItemBindStatus.NOT_BOUND: 0>
BOUND_TO_CHARACTER = <ItemBindStatus.BOUND_TO_CHARACTER: 1>
BOUND_TO_ACCOUNT = <ItemBindStatus.BOUND_TO_ACCOUNT: 2>
BOUNT_TO_GUILD = <ItemBindStatus.BOUNT_TO_GUILD: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemLocation(builtins.int, aiobungie.Enum):
729@typing.final
730class ItemLocation(int, Enum):
731    """An enum for Destiny 2 items location."""
732
733    UNKNOWN = 0
734    INVENTORY = 1
735    VAULT = 2
736    VENDOR = 3
737    POSTMASTER = 4

An enum for Destiny 2 items location.

UNKNOWN = <ItemLocation.UNKNOWN: 0>
INVENTORY = <ItemLocation.INVENTORY: 1>
VAULT = <ItemLocation.VAULT: 2>
VENDOR = <ItemLocation.VENDOR: 3>
POSTMASTER = <ItemLocation.POSTMASTER: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemState(aiobungie.Flag):
754@typing.final
755class ItemState(Flag):
756    """An enum for Destiny 2 item states."""
757
758    NONE = 0
759    LOCKED = 1 << 0
760    TRACKED = 1 << 1
761    MASTERWORKED = 1 << 2
762    CRAFTED = 1 << 3
763    """If this bit is set, the item has been 'crafted' by the player."""
764    HIGHLITED_OBJECTIVE = 1 << 4
765    """If this bit is set, the item is a 'highlighted' objective."""

An enum for Destiny 2 item states.

NONE = <ItemState.NONE: 0>
LOCKED = <ItemState.LOCKED: 1>
TRACKED = <ItemState.TRACKED: 2>
MASTERWORKED = <ItemState.MASTERWORKED: 4>
CRAFTED = <ItemState.CRAFTED: 8>

If this bit is set, the item has been 'crafted' by the player.

HIGHLITED_OBJECTIVE = <ItemState.HIGHLITED_OBJECTIVE: 16>

If this bit is set, the item is a 'highlighted' objective.

Inherited Members
Flag
name
value
@typing.final
class ItemSubType(builtins.int, aiobungie.Enum):
586@typing.final
587class ItemSubType(int, Enum):
588    """An enum for Destiny 2 inventory items subtype."""
589
590    NONE = 0
591    AUTORIFLE = 6
592    SHOTGUN = 7
593    MACHINEGUN = 8
594    HANDCANNON = 9
595    ROCKETLAUNCHER = 10
596    FUSIONRIFLE = 11
597    SNIPERRIFLE = 12
598    PULSERIFLE = 13
599    SCOUTRIFLE = 14
600    SIDEARM = 17
601    SWORD = 18
602    MASK = 19
603    SHADER = 20
604    ORNAMENT = 21
605    FUSIONRIFLELINE = 22
606    GRENADELAUNCHER = 23
607    SUBMACHINEGUN = 24
608    TRACERIFLE = 25
609    HELMETARMOR = 26
610    GAUNTLETSARMOR = 27
611    CHESTARMOR = 28
612    LEGARMOR = 29
613    CLASSARMOR = 30
614    BOW = 31
615    DUMMYREPEATABLEBOUNTY = 32

An enum for Destiny 2 inventory items subtype.

NONE = <ItemSubType.NONE: 0>
AUTORIFLE = <ItemSubType.AUTORIFLE: 6>
SHOTGUN = <ItemSubType.SHOTGUN: 7>
MACHINEGUN = <ItemSubType.MACHINEGUN: 8>
HANDCANNON = <ItemSubType.HANDCANNON: 9>
ROCKETLAUNCHER = <ItemSubType.ROCKETLAUNCHER: 10>
FUSIONRIFLE = <ItemSubType.FUSIONRIFLE: 11>
SNIPERRIFLE = <ItemSubType.SNIPERRIFLE: 12>
PULSERIFLE = <ItemSubType.PULSERIFLE: 13>
SCOUTRIFLE = <ItemSubType.SCOUTRIFLE: 14>
SIDEARM = <ItemSubType.SIDEARM: 17>
SWORD = <ItemSubType.SWORD: 18>
MASK = <ItemSubType.MASK: 19>
SHADER = <ItemSubType.SHADER: 20>
ORNAMENT = <ItemSubType.ORNAMENT: 21>
FUSIONRIFLELINE = <ItemSubType.FUSIONRIFLELINE: 22>
GRENADELAUNCHER = <ItemSubType.GRENADELAUNCHER: 23>
SUBMACHINEGUN = <ItemSubType.SUBMACHINEGUN: 24>
TRACERIFLE = <ItemSubType.TRACERIFLE: 25>
HELMETARMOR = <ItemSubType.HELMETARMOR: 26>
GAUNTLETSARMOR = <ItemSubType.GAUNTLETSARMOR: 27>
CHESTARMOR = <ItemSubType.CHESTARMOR: 28>
LEGARMOR = <ItemSubType.LEGARMOR: 29>
CLASSARMOR = <ItemSubType.CLASSARMOR: 30>
BOW = <ItemSubType.BOW: 31>
DUMMYREPEATABLEBOUNTY = <ItemSubType.DUMMYREPEATABLEBOUNTY: 32>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemTier(builtins.int, aiobungie.Enum):
618@typing.final
619class ItemTier(int, Enum):
620    """An enum for a Destiny 2 item tier."""
621
622    NONE = 0
623    BASIC = 3340296461
624    COMMON = 2395677314
625    RARE = 2127292149
626    LEGENDERY = 4008398120
627    EXOTIC = 2759499571

An enum for a Destiny 2 item tier.

NONE = <ItemTier.NONE: 0>
BASIC = <ItemTier.BASIC: 3340296461>
COMMON = <ItemTier.COMMON: 2395677314>
RARE = <ItemTier.RARE: 2127292149>
LEGENDERY = <ItemTier.LEGENDERY: 4008398120>
EXOTIC = <ItemTier.EXOTIC: 2759499571>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemType(builtins.int, aiobungie.Enum):
553@typing.final
554class ItemType(int, Enum):
555    """Enums for Destiny2's item types."""
556
557    NONE = 0
558    CURRENCY = 1
559    ARMOR = 2
560    WEAPON = 3
561    MESSAGE = 7
562    ENGRAM = 8
563    CONSUMABLE = 9
564    EXCHANGEMATERIAL = 10
565    MISSIONREWARD = 11
566    QUESTSTEP = 12
567    QUESTSTEPCOMPLETE = 13
568    EMBLEM = 14
569    QUEST = 15
570    SUBCLASS = 16
571    CLANBANNER = 17
572    AURA = 18
573    MOD = 19
574    DUMMY = 20
575    SHIP = 21
576    VEHICLE = 22
577    EMOTE = 23
578    GHOST = 24
579    PACKAGE = 25
580    BOUNTY = 26
581    WRAPPER = 27
582    SEASONALARTIFACT = 28
583    FINISHER = 29

Enums for Destiny2's item types.

NONE = <ItemType.NONE: 0>
CURRENCY = <ItemType.CURRENCY: 1>
ARMOR = <ItemType.ARMOR: 2>
WEAPON = <ItemType.WEAPON: 3>
MESSAGE = <ItemType.MESSAGE: 7>
ENGRAM = <ItemType.ENGRAM: 8>
CONSUMABLE = <ItemType.CONSUMABLE: 9>
EXCHANGEMATERIAL = <ItemType.EXCHANGEMATERIAL: 10>
MISSIONREWARD = <ItemType.MISSIONREWARD: 11>
QUESTSTEP = <ItemType.QUESTSTEP: 12>
QUESTSTEPCOMPLETE = <ItemType.QUESTSTEPCOMPLETE: 13>
EMBLEM = <ItemType.EMBLEM: 14>
QUEST = <ItemType.QUEST: 15>
SUBCLASS = <ItemType.SUBCLASS: 16>
CLANBANNER = <ItemType.CLANBANNER: 17>
AURA = <ItemType.AURA: 18>
MOD = <ItemType.MOD: 19>
DUMMY = <ItemType.DUMMY: 20>
SHIP = <ItemType.SHIP: 21>
VEHICLE = <ItemType.VEHICLE: 22>
EMOTE = <ItemType.EMOTE: 23>
GHOST = <ItemType.GHOST: 24>
PACKAGE = <ItemType.PACKAGE: 25>
BOUNTY = <ItemType.BOUNTY: 26>
WRAPPER = <ItemType.WRAPPER: 27>
SEASONALARTIFACT = <ItemType.SEASONALARTIFACT: 28>
FINISHER = <ItemType.FINISHER: 29>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Iterator(typing.Generic[~Item]):
 45class Iterator(typing.Generic[Item]):
 46    """A Flat, In-Memory iterator for sequenced based data.
 47
 48    Example
 49    -------
 50    ```py
 51    iterator = Iterator([1, 2, 3])
 52
 53    # Map the results.
 54    for item in iterator.map(lambda item: item * 2):
 55        print(item)
 56    # 2
 57    # 4
 58
 59    # Indexing is also supported.
 60    print(iterator[0])
 61    # 1
 62
 63    # Normal iteration.
 64    for item in iterator:
 65        print(item)
 66    # 1
 67    # 2
 68    # 3
 69
 70    # Union two iterators.
 71    iterator2 = Iterator([4, 5, 6])
 72    final = iterator | iterator2
 73    # <Iterator([1, 2, 3, 4, 5, 6])>
 74    ```
 75
 76    Parameters
 77    ----------
 78    items: `collections.Iterable[Item]`
 79        The items to iterate over.
 80    """
 81
 82    __slots__ = ("_items",)
 83
 84    def __init__(self, items: collections.Iterable[Item]) -> None:
 85        self._items = iter(items)
 86
 87    @typing.overload
 88    def collect(self) -> list[Item]:
 89        ...
 90
 91    @typing.overload
 92    def collect(self, casting: _B) -> list[_B]:
 93        ...
 94
 95    def collect(
 96        self, casting: typing.Optional[_B] = None
 97    ) -> typing.Union[list[Item], list[_B]]:
 98        """Collects all items in the iterator into a list and cast them into an object if provided.
 99
100        Example
101        -------
102        >>> iterator = Iterator([1, 2, 3])
103        >>> iterator.collect(casting=str)
104        ["1", "2", "3"]
105
106        Parameters
107        ----------
108        casting: `T | None`
109            The type to cast the items to. If `None` is provided, the items will be returned as is.
110
111        Raises
112        ------
113        `StopIteration`
114            If no elements are left in the iterator.
115        """
116        if casting is not None:
117            return typing.cast(list[_B], list(map(casting, self._items)))
118
119        return list(self._items)
120
121    def next(self) -> Item:
122        """Returns the next item in the iterator.
123
124        Example
125        -------
126        ```py
127        iterator = Iterator(["1", "2", "3"])
128        item = iterator.next()
129        assert item == "1"
130        item = iterator.next()
131        assert item == "2"
132        ```
133
134        Raises
135        ------
136        `StopIteration`
137            If no elements are left in the iterator.
138        """
139        try:
140            return self.__next__()
141        except StopIteration:
142            self._ok()
143
144    def map(
145        self, predicate: collections.Callable[[Item], OtherItem]
146    ) -> Iterator[OtherItem]:
147        """Maps each item in the iterator to its predicated value.
148
149        Example
150        -------
151        ```py
152        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
153        print(iterator)
154        # <Iterator([1, 2, 3])>
155        ```
156
157        Parameters
158        ----------
159        predicate: `collections.Callable[[Item], OtherItem]`
160            The function to map each item in the iterator to its predicated value.
161
162        Returns
163        -------
164        `Iterator[OtherItem]`
165            The mapped iterator.
166
167        Raises
168        ------
169        `StopIteration`
170            If no elements are left in the iterator.
171        """
172        return Iterator(map(predicate, self._items))
173
174    def take(self, n: int) -> Iterator[Item]:
175        """Take the first number of items until the number of items are yielded or
176        the end of the iterator is reached.
177
178        Example
179        -------
180        ```py
181        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
182        print(iterator.take(2))
183        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
184        ```
185
186        Parameters
187        ----------
188        n: `int`
189            The number of items to take.
190
191        Raises
192        ------
193        `StopIteration`
194            If no elements are left in the iterator.
195        """
196        return Iterator(itertools.islice(self._items, n))
197
198    def take_while(
199        self, predicate: collections.Callable[[Item], bool]
200    ) -> Iterator[Item]:
201        """Yields items from the iterator while predicate returns `True`.
202
203        Example
204        -------
205        ```py
206        iterator = Iterator([STEAM, XBOX, STADIA])
207        print(iterator.take_while(lambda platform: platform is not XBOX))
208        # <Iterator([STEAM])>
209        ```
210
211        Parameters
212        ----------
213        predicate: `collections.Callable[[Item], bool]`
214            The function to predicate each item in the iterator.
215
216        Raises
217        ------
218        `StopIteration`
219            If no elements are left in the iterator.
220        """
221        return Iterator(itertools.takewhile(predicate, self._items))
222
223    def drop_while(
224        self, predicate: collections.Callable[[Item], bool]
225    ) -> Iterator[Item]:
226        """Yields items from the iterator while predicate returns `False`.
227
228        Example
229        -------
230        ```py
231        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
232        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
233        # <Iterator([DestinyMembership(name="Bob")])>
234        ```
235
236        Parameters
237        ----------
238        predicate: `collections.Callable[[Item], bool]`
239            The function to predicate each item in the iterator.
240
241        Raises
242        ------
243        `StopIteration`
244            If no elements are left in the iterator.
245        """
246        return Iterator(itertools.dropwhile(predicate, self._items))
247
248    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
249        """Filters the iterator to only yield items that match the predicate.
250
251        Example
252        -------
253        ```py
254        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
255        print(names.filter(lambda n: n != "Jim"))
256        # <Iterator(["Bob", "Mike", "Jess"])>
257        ```
258        """
259        return Iterator(filter(predicate, self._items))
260
261    def skip(self, n: int) -> Iterator[Item]:
262        """Skips the first number of items in the iterator.
263
264        Example
265        -------
266        ```py
267        iterator = Iterator([STEAM, XBOX, STADIA])
268        print(iterator.skip(1))
269        # <Iterator([XBOX, STADIA])>
270        ```
271        """
272        return Iterator(itertools.islice(self._items, n, None))
273
274    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
275        """Zips the iterator with another iterable.
276
277        Example
278        -------
279        ```py
280        iterator = Iterator([1, 3, 5])
281        other = Iterator([2, 4, 6])
282        for item, other_item in iterator.zip(other):
283            print(item, other_item)
284        # <Iterator([(1, 2), (3, 4), (5, 6)])>
285        ```
286
287        Parameters
288        ----------
289        other: `Iterator[OtherItem]`
290            The iterable to zip with.
291
292        Raises
293        ------
294        `StopIteration`
295            If no elements are left in the iterator.
296        """
297        return Iterator(zip(self._items, other))
298
299    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
300        """`True` if all items in the iterator match the predicate.
301
302        Example
303        -------
304        ```py
305        iterator = Iterator([1, 2, 3])
306        while iterator.all(lambda item: isinstance(item, int)):
307            print("Still all integers")
308            continue
309        # Still all integers
310        ```
311
312        Parameters
313        ----------
314        predicate: `collections.Callable[[Item], bool]`
315            The function to test each item in the iterator.
316
317        Raises
318        ------
319        `StopIteration`
320            If no elements are left in the iterator.
321        """
322        return all(predicate(item) for item in self)
323
324    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
325        """`True` if any items in the iterator match the predicate.
326
327        Example
328        -------
329        ```py
330        iterator = Iterator([1, 2, 3])
331        if iterator.any(lambda item: isinstance(item, int)):
332            print("At least one item is an int.")
333        # At least one item is an int.
334        ```
335
336        Parameters
337        ----------
338        predicate: `collections.Callable[[Item], bool]`
339            The function to test each item in the iterator.
340
341        Raises
342        ------
343        `StopIteration`
344            If no elements are left in the iterator.
345        """
346        return any(predicate(item) for item in self)
347
348    def sort(
349        self,
350        *,
351        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
352        reverse: bool = False,
353    ) -> Iterator[Item]:
354        """Sorts the iterator.
355
356        Example
357        -------
358        ```py
359        iterator = Iterator([3, 1, 6, 7])
360        print(iterator.sort(key=lambda item: item))
361        # <Iterator([1, 3, 6, 7])>
362        ```
363
364        Parameters
365        ----------
366        key: `collections.Callable[[Item], Any]`
367            The function to sort by.
368        reverse: `bool`
369            Whether to reverse the sort.
370
371        Raises
372        ------
373        `StopIteration`
374            If no elements are left in the iterator.
375        """
376        return Iterator(sorted(self._items, key=key, reverse=reverse))
377
378    def first(self) -> Item:
379        """Returns the first item in the iterator.
380
381        Example
382        -------
383        ```py
384        iterator = Iterator([3, 1, 6, 7])
385        print(iterator.first())
386        3
387        ```
388
389        Raises
390        ------
391        `StopIteration`
392            If no elements are left in the iterator.
393        """
394        return self.take(1).next()
395
396    def reversed(self) -> Iterator[Item]:
397        """Returns a new iterator that yields the items in the iterator in reverse order.
398
399        Example
400        -------
401        ```py
402        iterator = Iterator([3, 1, 6, 7])
403        print(iterator.reversed())
404        # <Iterator([7, 6, 1, 3])>
405        ```
406
407        Raises
408        ------
409        `StopIteration`
410            If no elements are left in the iterator.
411        """
412        return Iterator(reversed(self.collect()))
413
414    def count(self) -> int:
415        """Returns the number of items in the iterator.
416
417        Example
418        -------
419        ```py
420        iterator = Iterator([3, 1, 6, 7])
421        print(iterator.count())
422        4
423        ```
424        """
425        count = 0
426        for _ in self:
427            count += 1
428
429        return count
430
431    def union(self, other: Iterator[Item]) -> Iterator[Item]:
432        """Returns a new iterator that yields all items from both iterators.
433
434        Example
435        -------
436        ```py
437        iterator = Iterator([1, 2, 3])
438        other = Iterator([4, 5, 6])
439        print(iterator.union(other))
440        # <Iterator([1, 2, 3, 4, 5, 6])>
441        ```
442
443        Parameters
444        ----------
445        other: `Iterator[Item]`
446            The iterable to union with.
447
448        Raises
449        ------
450        `StopIteration`
451            If no elements are left in the iterator.
452        """
453        return Iterator(itertools.chain(self._items, other))
454
455    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
456        """Calls the function on each item in the iterator.
457
458        Example
459        -------
460        ```py
461        iterator = Iterator([1, 2, 3])
462        iterator.for_each(lambda item: print(item))
463        # 1
464        # 2
465        # 3
466        ```
467
468        Parameters
469        ----------
470        func: `typeshed.Callable[[Item], None]`
471            The function to call on each item in the iterator.
472        """
473        for item in self:
474            func(item)
475
476    async def async_for_each(
477        self,
478        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
479    ) -> None:
480        """Calls the async function on each item in the iterator concurrently.
481
482        Example
483        -------
484        ```py
485        async def signup(username: str) -> None:
486            async with aiohttp.request('POST', '...') as r:
487                # Actual logic.
488                ...
489
490        async def main():
491            users = aiobungie.into_iter(["user_danny", "user_jojo"])
492            await users.async_for_each(lambda username: signup(username))
493        ```
494
495        Parameters
496        ----------
497        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
498            The async function to call on each item in the iterator.
499        """
500        await _helpers.awaits(*(func(item) for item in self))
501
502    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
503        """Returns a new iterator that yields tuples of the index and item.
504
505        Example
506        -------
507        ```py
508        iterator = Iterator([1, 2, 3])
509        for index, item in iterator.enumerate():
510            print(index, item)
511        # 0 1
512        # 1 2
513        # 2 3
514        ```
515
516        Raises
517        ------
518        `StopIteration`
519            If no elements are left in the iterator.
520        """
521        return Iterator(enumerate(self._items, start=start))
522
523    def _ok(self) -> typing.NoReturn:
524        raise StopIteration("No more items in the iterator.") from None
525
526    def __getitem__(self, index: int) -> Item:
527        try:
528            return self.skip(index).first()
529        except IndexError:
530            self._ok()
531
532    def __or__(self, other: Iterator[Item]) -> Iterator[Item]:
533        return self.union(other)
534
535    # This is a never.
536    def __setitem__(self) -> typing.NoReturn:
537        raise TypeError(
538            f"{type(self).__name__} doesn't support item assignment."
539        ) from None
540
541    def __repr__(self) -> str:
542        return f'<{self.__class__.__name__}({", ".join([str(item) for item in self])})>'
543
544    def __len__(self) -> int:
545        return self.count()
546
547    def __iter__(self) -> Iterator[Item]:
548        return self
549
550    def __next__(self) -> Item:
551        try:
552            item = next(self._items)
553        except StopIteration:
554            self._ok()
555
556        return item

A Flat, In-Memory iterator for sequenced based data.

Example
iterator = Iterator([1, 2, 3])

# Map the results.
for item in iterator.map(lambda item: item * 2):
    print(item)
# 2
# 4

# Indexing is also supported.
print(iterator[0])
# 1

# Normal iteration.
for item in iterator:
    print(item)
# 1
# 2
# 3

# Union two iterators.
iterator2 = Iterator([4, 5, 6])
final = iterator | iterator2
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • items (collections.Iterable[Item]): The items to iterate over.
Iterator(items: collections.abc.Iterable[~Item])
84    def __init__(self, items: collections.Iterable[Item]) -> None:
85        self._items = iter(items)
def collect( self, casting: 'typing.Optional[_B]' = None) -> 'typing.Union[list[Item], list[_B]]':
 95    def collect(
 96        self, casting: typing.Optional[_B] = None
 97    ) -> typing.Union[list[Item], list[_B]]:
 98        """Collects all items in the iterator into a list and cast them into an object if provided.
 99
100        Example
101        -------
102        >>> iterator = Iterator([1, 2, 3])
103        >>> iterator.collect(casting=str)
104        ["1", "2", "3"]
105
106        Parameters
107        ----------
108        casting: `T | None`
109            The type to cast the items to. If `None` is provided, the items will be returned as is.
110
111        Raises
112        ------
113        `StopIteration`
114            If no elements are left in the iterator.
115        """
116        if casting is not None:
117            return typing.cast(list[_B], list(map(casting, self._items)))
118
119        return list(self._items)

Collects all items in the iterator into a list and cast them into an object if provided.

Example
>>> iterator = Iterator([1, 2, 3])
>>> iterator.collect(casting=str)
["1", "2", "3"]
Parameters
  • casting (T | None): The type to cast the items to. If None is provided, the items will be returned as is.
Raises
  • StopIteration: If no elements are left in the iterator.
def next(self) -> ~Item:
121    def next(self) -> Item:
122        """Returns the next item in the iterator.
123
124        Example
125        -------
126        ```py
127        iterator = Iterator(["1", "2", "3"])
128        item = iterator.next()
129        assert item == "1"
130        item = iterator.next()
131        assert item == "2"
132        ```
133
134        Raises
135        ------
136        `StopIteration`
137            If no elements are left in the iterator.
138        """
139        try:
140            return self.__next__()
141        except StopIteration:
142            self._ok()

Returns the next item in the iterator.

Example
iterator = Iterator(["1", "2", "3"])
item = iterator.next()
assert item == "1"
item = iterator.next()
assert item == "2"
Raises
  • StopIteration: If no elements are left in the iterator.
def map( self, predicate: 'collections.Callable[[Item], OtherItem]') -> 'Iterator[OtherItem]':
144    def map(
145        self, predicate: collections.Callable[[Item], OtherItem]
146    ) -> Iterator[OtherItem]:
147        """Maps each item in the iterator to its predicated value.
148
149        Example
150        -------
151        ```py
152        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
153        print(iterator)
154        # <Iterator([1, 2, 3])>
155        ```
156
157        Parameters
158        ----------
159        predicate: `collections.Callable[[Item], OtherItem]`
160            The function to map each item in the iterator to its predicated value.
161
162        Returns
163        -------
164        `Iterator[OtherItem]`
165            The mapped iterator.
166
167        Raises
168        ------
169        `StopIteration`
170            If no elements are left in the iterator.
171        """
172        return Iterator(map(predicate, self._items))

Maps each item in the iterator to its predicated value.

Example
iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
print(iterator)
# <Iterator([1, 2, 3])>
Parameters
  • predicate (collections.Callable[[Item], OtherItem]): The function to map each item in the iterator to its predicated value.
Returns
  • Iterator[OtherItem]: The mapped iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def take(self, n: int) -> aiobungie.Iterator[~Item]:
174    def take(self, n: int) -> Iterator[Item]:
175        """Take the first number of items until the number of items are yielded or
176        the end of the iterator is reached.
177
178        Example
179        -------
180        ```py
181        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
182        print(iterator.take(2))
183        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
184        ```
185
186        Parameters
187        ----------
188        n: `int`
189            The number of items to take.
190
191        Raises
192        ------
193        `StopIteration`
194            If no elements are left in the iterator.
195        """
196        return Iterator(itertools.islice(self._items, n))

Take the first number of items until the number of items are yielded or the end of the iterator is reached.

Example
iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
print(iterator.take(2))
# <Iterator([GameMode.RAID, GameMode.STRIKE])>
Parameters
  • n (int): The number of items to take.
Raises
  • StopIteration: If no elements are left in the iterator.
def take_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.Iterator[~Item]:
198    def take_while(
199        self, predicate: collections.Callable[[Item], bool]
200    ) -> Iterator[Item]:
201        """Yields items from the iterator while predicate returns `True`.
202
203        Example
204        -------
205        ```py
206        iterator = Iterator([STEAM, XBOX, STADIA])
207        print(iterator.take_while(lambda platform: platform is not XBOX))
208        # <Iterator([STEAM])>
209        ```
210
211        Parameters
212        ----------
213        predicate: `collections.Callable[[Item], bool]`
214            The function to predicate each item in the iterator.
215
216        Raises
217        ------
218        `StopIteration`
219            If no elements are left in the iterator.
220        """
221        return Iterator(itertools.takewhile(predicate, self._items))

Yields items from the iterator while predicate returns True.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.take_while(lambda platform: platform is not XBOX))
# <Iterator([STEAM])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def drop_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.Iterator[~Item]:
223    def drop_while(
224        self, predicate: collections.Callable[[Item], bool]
225    ) -> Iterator[Item]:
226        """Yields items from the iterator while predicate returns `False`.
227
228        Example
229        -------
230        ```py
231        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
232        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
233        # <Iterator([DestinyMembership(name="Bob")])>
234        ```
235
236        Parameters
237        ----------
238        predicate: `collections.Callable[[Item], bool]`
239            The function to predicate each item in the iterator.
240
241        Raises
242        ------
243        `StopIteration`
244            If no elements are left in the iterator.
245        """
246        return Iterator(itertools.dropwhile(predicate, self._items))

Yields items from the iterator while predicate returns False.

Example
iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
# <Iterator([DestinyMembership(name="Bob")])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def filter( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.Iterator[~Item]:
248    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
249        """Filters the iterator to only yield items that match the predicate.
250
251        Example
252        -------
253        ```py
254        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
255        print(names.filter(lambda n: n != "Jim"))
256        # <Iterator(["Bob", "Mike", "Jess"])>
257        ```
258        """
259        return Iterator(filter(predicate, self._items))

Filters the iterator to only yield items that match the predicate.

Example
names = Iterator(["Jim", "Bob", "Mike", "Jess"])
print(names.filter(lambda n: n != "Jim"))
# <Iterator(["Bob", "Mike", "Jess"])>
def skip(self, n: int) -> aiobungie.Iterator[~Item]:
261    def skip(self, n: int) -> Iterator[Item]:
262        """Skips the first number of items in the iterator.
263
264        Example
265        -------
266        ```py
267        iterator = Iterator([STEAM, XBOX, STADIA])
268        print(iterator.skip(1))
269        # <Iterator([XBOX, STADIA])>
270        ```
271        """
272        return Iterator(itertools.islice(self._items, n, None))

Skips the first number of items in the iterator.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.skip(1))
# <Iterator([XBOX, STADIA])>
def zip(self, other: 'Iterator[OtherItem]') -> 'Iterator[tuple[Item, OtherItem]]':
274    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
275        """Zips the iterator with another iterable.
276
277        Example
278        -------
279        ```py
280        iterator = Iterator([1, 3, 5])
281        other = Iterator([2, 4, 6])
282        for item, other_item in iterator.zip(other):
283            print(item, other_item)
284        # <Iterator([(1, 2), (3, 4), (5, 6)])>
285        ```
286
287        Parameters
288        ----------
289        other: `Iterator[OtherItem]`
290            The iterable to zip with.
291
292        Raises
293        ------
294        `StopIteration`
295            If no elements are left in the iterator.
296        """
297        return Iterator(zip(self._items, other))

Zips the iterator with another iterable.

Example
iterator = Iterator([1, 3, 5])
other = Iterator([2, 4, 6])
for item, other_item in iterator.zip(other):
    print(item, other_item)
# <Iterator([(1, 2), (3, 4), (5, 6)])>
Parameters
  • other (Iterator[OtherItem]): The iterable to zip with.
Raises
  • StopIteration: If no elements are left in the iterator.
def all(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
299    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
300        """`True` if all items in the iterator match the predicate.
301
302        Example
303        -------
304        ```py
305        iterator = Iterator([1, 2, 3])
306        while iterator.all(lambda item: isinstance(item, int)):
307            print("Still all integers")
308            continue
309        # Still all integers
310        ```
311
312        Parameters
313        ----------
314        predicate: `collections.Callable[[Item], bool]`
315            The function to test each item in the iterator.
316
317        Raises
318        ------
319        `StopIteration`
320            If no elements are left in the iterator.
321        """
322        return all(predicate(item) for item in self)

True if all items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
    print("Still all integers")
    continue
# Still all integers
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def any(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
324    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
325        """`True` if any items in the iterator match the predicate.
326
327        Example
328        -------
329        ```py
330        iterator = Iterator([1, 2, 3])
331        if iterator.any(lambda item: isinstance(item, int)):
332            print("At least one item is an int.")
333        # At least one item is an int.
334        ```
335
336        Parameters
337        ----------
338        predicate: `collections.Callable[[Item], bool]`
339            The function to test each item in the iterator.
340
341        Raises
342        ------
343        `StopIteration`
344            If no elements are left in the iterator.
345        """
346        return any(predicate(item) for item in self)

True if any items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
    print("At least one item is an int.")
# At least one item is an int.
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def sort( self, *, key: 'collections.Callable[[Item], typeshed.SupportsRichComparison]', reverse: bool = False) -> aiobungie.Iterator[~Item]:
348    def sort(
349        self,
350        *,
351        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
352        reverse: bool = False,
353    ) -> Iterator[Item]:
354        """Sorts the iterator.
355
356        Example
357        -------
358        ```py
359        iterator = Iterator([3, 1, 6, 7])
360        print(iterator.sort(key=lambda item: item))
361        # <Iterator([1, 3, 6, 7])>
362        ```
363
364        Parameters
365        ----------
366        key: `collections.Callable[[Item], Any]`
367            The function to sort by.
368        reverse: `bool`
369            Whether to reverse the sort.
370
371        Raises
372        ------
373        `StopIteration`
374            If no elements are left in the iterator.
375        """
376        return Iterator(sorted(self._items, key=key, reverse=reverse))

Sorts the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.sort(key=lambda item: item))
# <Iterator([1, 3, 6, 7])>
Parameters
  • key (collections.Callable[[Item], Any]): The function to sort by.
  • reverse (bool): Whether to reverse the sort.
Raises
  • StopIteration: If no elements are left in the iterator.
def first(self) -> ~Item:
378    def first(self) -> Item:
379        """Returns the first item in the iterator.
380
381        Example
382        -------
383        ```py
384        iterator = Iterator([3, 1, 6, 7])
385        print(iterator.first())
386        3
387        ```
388
389        Raises
390        ------
391        `StopIteration`
392            If no elements are left in the iterator.
393        """
394        return self.take(1).next()

Returns the first item in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.first())
3
Raises
  • StopIteration: If no elements are left in the iterator.
def reversed(self) -> aiobungie.Iterator[~Item]:
396    def reversed(self) -> Iterator[Item]:
397        """Returns a new iterator that yields the items in the iterator in reverse order.
398
399        Example
400        -------
401        ```py
402        iterator = Iterator([3, 1, 6, 7])
403        print(iterator.reversed())
404        # <Iterator([7, 6, 1, 3])>
405        ```
406
407        Raises
408        ------
409        `StopIteration`
410            If no elements are left in the iterator.
411        """
412        return Iterator(reversed(self.collect()))

Returns a new iterator that yields the items in the iterator in reverse order.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.reversed())
# <Iterator([7, 6, 1, 3])>
Raises
  • StopIteration: If no elements are left in the iterator.
def count(self) -> int:
414    def count(self) -> int:
415        """Returns the number of items in the iterator.
416
417        Example
418        -------
419        ```py
420        iterator = Iterator([3, 1, 6, 7])
421        print(iterator.count())
422        4
423        ```
424        """
425        count = 0
426        for _ in self:
427            count += 1
428
429        return count

Returns the number of items in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.count())
4
def union( self, other: aiobungie.Iterator[~Item]) -> aiobungie.Iterator[~Item]:
431    def union(self, other: Iterator[Item]) -> Iterator[Item]:
432        """Returns a new iterator that yields all items from both iterators.
433
434        Example
435        -------
436        ```py
437        iterator = Iterator([1, 2, 3])
438        other = Iterator([4, 5, 6])
439        print(iterator.union(other))
440        # <Iterator([1, 2, 3, 4, 5, 6])>
441        ```
442
443        Parameters
444        ----------
445        other: `Iterator[Item]`
446            The iterable to union with.
447
448        Raises
449        ------
450        `StopIteration`
451            If no elements are left in the iterator.
452        """
453        return Iterator(itertools.chain(self._items, other))

Returns a new iterator that yields all items from both iterators.

Example
iterator = Iterator([1, 2, 3])
other = Iterator([4, 5, 6])
print(iterator.union(other))
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • other (Iterator[Item]): The iterable to union with.
Raises
  • StopIteration: If no elements are left in the iterator.
def for_each(self, func: collections.abc.Callable[[~Item], typing.Any]) -> None:
455    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
456        """Calls the function on each item in the iterator.
457
458        Example
459        -------
460        ```py
461        iterator = Iterator([1, 2, 3])
462        iterator.for_each(lambda item: print(item))
463        # 1
464        # 2
465        # 3
466        ```
467
468        Parameters
469        ----------
470        func: `typeshed.Callable[[Item], None]`
471            The function to call on each item in the iterator.
472        """
473        for item in self:
474            func(item)

Calls the function on each item in the iterator.

Example
iterator = Iterator([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
  • func (typeshed.Callable[[Item], None]): The function to call on each item in the iterator.
async def async_for_each( self, func: collections.abc.Callable[[~Item], collections.abc.Coroutine[None, None, None]]) -> None:
476    async def async_for_each(
477        self,
478        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
479    ) -> None:
480        """Calls the async function on each item in the iterator concurrently.
481
482        Example
483        -------
484        ```py
485        async def signup(username: str) -> None:
486            async with aiohttp.request('POST', '...') as r:
487                # Actual logic.
488                ...
489
490        async def main():
491            users = aiobungie.into_iter(["user_danny", "user_jojo"])
492            await users.async_for_each(lambda username: signup(username))
493        ```
494
495        Parameters
496        ----------
497        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
498            The async function to call on each item in the iterator.
499        """
500        await _helpers.awaits(*(func(item) for item in self))

Calls the async function on each item in the iterator concurrently.

Example
async def signup(username: str) -> None:
    async with aiohttp.request('POST', '...') as r:
        # Actual logic.
        ...

async def main():
    users = aiobungie.into_iter(["user_danny", "user_jojo"])
    await users.async_for_each(lambda username: signup(username))
Parameters
  • func (collections.Callable[[Item], collections.Coroutine[None, None, None]]): The async function to call on each item in the iterator.
def enumerate( self, *, start: int = 0) -> aiobungie.Iterator[tuple[int, ~Item]]:
502    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
503        """Returns a new iterator that yields tuples of the index and item.
504
505        Example
506        -------
507        ```py
508        iterator = Iterator([1, 2, 3])
509        for index, item in iterator.enumerate():
510            print(index, item)
511        # 0 1
512        # 1 2
513        # 2 3
514        ```
515
516        Raises
517        ------
518        `StopIteration`
519            If no elements are left in the iterator.
520        """
521        return Iterator(enumerate(self._items, start=start))

Returns a new iterator that yields tuples of the index and item.

Example
iterator = Iterator([1, 2, 3])
for index, item in iterator.enumerate():
    print(index, item)
# 0 1
# 1 2
# 2 3
Raises
  • StopIteration: If no elements are left in the iterator.
@typing.final
class MembershipOption(builtins.int, aiobungie.Enum):
710@typing.final
711class MembershipOption(int, Enum):
712    """A enum for GroupV2 membership options."""
713
714    REVIEWD = 0
715    OPEN = 1
716    CLOSED = 2

A enum for GroupV2 membership options.

REVIEWD = <MembershipOption.REVIEWD: 0>
OPEN = <MembershipOption.OPEN: 1>
CLOSED = <MembershipOption.CLOSED: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class MembershipType(builtins.int, aiobungie.Enum):
458@typing.final
459class MembershipType(int, Enum):
460    """An Enum for Bungie membership types."""
461
462    NONE = 0
463    XBOX = 1
464    PSN = 2
465    STEAM = 3
466    BLIZZARD = 4
467    STADIA = 5
468    EPIC_GAMES_STORE = 6
469    DEMON = 10
470    BUNGIE = 254
471    ALL = -1

An Enum for Bungie membership types.

NONE = <MembershipType.NONE: 0>
XBOX = <MembershipType.XBOX: 1>
PSN = <MembershipType.PSN: 2>
STEAM = <MembershipType.STEAM: 3>
BLIZZARD = <MembershipType.BLIZZARD: 4>
STADIA = <MembershipType.STADIA: 5>
EPIC_GAMES_STORE = <MembershipType.EPIC_GAMES_STORE: 6>
DEMON = <MembershipType.DEMON: 10>
BUNGIE = <MembershipType.BUNGIE: 254>
ALL = <MembershipType.ALL: -1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class MembershipTypeError(aiobungie.BadRequest):
182@attrs.define(auto_exc=True)
183class MembershipTypeError(BadRequest):
184    """A bad request error raised when passing wrong membership to the request.
185
186    Those fields are useful since it returns the correct membership and id which can be used
187    to make the request again with those fields.
188
189    Example
190    -------
191    ```py
192    try:
193        profile = await client.fetch_profile(
194            member_id=1,
195            type=aiobungie.MembershipType.STADIA,
196            components=[]
197        )
198
199    # Membership type is wrong!
200    except aiobungie.MembershipTypeError as err:
201        correct_membersip = err.into_membership()
202        profile_id = err.membership_id
203
204        # Recall the method.
205        profile = await client.fetch_profile(
206            member_id=profile_id,
207            type=correct_membership,
208            components=[]
209        )
210    ```
211    """
212
213    membership_type: str
214    """The errored membership type passed to the request."""
215
216    membership_id: int
217    """The errored user's membership id."""
218
219    required_membership: str
220    """The required correct membership for errored user."""
221
222    def into_membership(
223        self, value: typing.Optional[str] = None
224    ) -> enums.MembershipType:
225        """Turn the required membership from `str` into `aiobungie.Membership` type.
226
227        If value parameter is not provided it will fall back to the required membership.
228        """
229        if value is None:
230            return _DETERMINE_MSHIP(self.required_membership)
231        return _DETERMINE_MSHIP(value)
232
233    def __str__(self) -> str:
234        return (
235            f"Expected membership: {self.into_membership().name.replace('_', '').title()}, "
236            f"But got {self.into_membership(self.membership_type)} for id {self.membership_id}"
237        )
238
239    def __int__(self) -> int:
240        return int(self.membership_id)

A bad request error raised when passing wrong membership to the request.

Those fields are useful since it returns the correct membership and id which can be used to make the request again with those fields.

Example
try:
    profile = await client.fetch_profile(
        member_id=1,
        type=aiobungie.MembershipType.STADIA,
        components=[]
    )

# Membership type is wrong!
except aiobungie.MembershipTypeError as err:
    correct_membersip = err.into_membership()
    profile_id = err.membership_id

    # Recall the method.
    profile = await client.fetch_profile(
        member_id=profile_id,
        type=correct_membership,
        components=[]
    )
MembershipTypeError( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], membership_type: str, membership_id: int, required_membership: str)
 2def __init__(self, message, url, body, headers, membership_type, membership_id, required_membership):
 3    self.message = message
 4    self.url = url
 5    self.body = body
 6    self.headers = headers
 7    self.http_status = attr_dict['http_status'].default
 8    self.membership_type = membership_type
 9    self.membership_id = membership_id
10    self.required_membership = required_membership
11    BaseException.__init__(self, self.message,self.url,self.body,self.headers,self.membership_type,self.membership_id,self.required_membership)

Method generated by attrs for class MembershipTypeError.

membership_type: str

The errored membership type passed to the request.

membership_id: int

The errored user's membership id.

required_membership: str

The required correct membership for errored user.

def into_membership( self, value: Optional[str] = None) -> aiobungie.MembershipType:
222    def into_membership(
223        self, value: typing.Optional[str] = None
224    ) -> enums.MembershipType:
225        """Turn the required membership from `str` into `aiobungie.Membership` type.
226
227        If value parameter is not provided it will fall back to the required membership.
228        """
229        if value is None:
230            return _DETERMINE_MSHIP(self.required_membership)
231        return _DETERMINE_MSHIP(value)

Turn the required membership from str into aiobungie.Membership type.

If value parameter is not provided it will fall back to the required membership.

Inherited Members
BadRequest
url
body
headers
http_status
HTTPError
message
builtins.BaseException
with_traceback
@typing.final
class MilestoneType(builtins.int, aiobungie.Enum):
503@typing.final
504class MilestoneType(int, Enum):
505    """An Enum for Destiny 2 milestone types."""
506
507    UNKNOWN = 0
508    TUTORIAL = 1
509    ONETIME = 2
510    WEEKLY = 3
511    DAILY = 4
512    SPECIAL = 5

An Enum for Destiny 2 milestone types.

UNKNOWN = <MilestoneType.UNKNOWN: 0>
TUTORIAL = <MilestoneType.TUTORIAL: 1>
ONETIME = <MilestoneType.ONETIME: 2>
WEEKLY = <MilestoneType.WEEKLY: 3>
DAILY = <MilestoneType.DAILY: 4>
SPECIAL = <MilestoneType.SPECIAL: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class NotFound(aiobungie.HTTPException):
146@attrs.define(auto_exc=True)
147class NotFound(HTTPException):
148    """Raised when an unknown resource was not found."""
149
150    http_status: http.HTTPStatus = attrs.field(
151        default=http.HTTPStatus.NOT_FOUND, init=False
152    )

Raised when an unknown resource was not found.

NotFound( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class NotFound.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class ObjectiveUIStyle(builtins.int, aiobungie.Enum):
 94@typing.final
 95class ObjectiveUIStyle(int, enums.Enum):
 96    NONE = 0
 97    HIGHLIGHTED = 1
 98    CRAFTING_WEAPON_LEVEL = 2
 99    CRAFTING_WEAPON_LEVEL_PROGRESS = 3
100    CRAFTING_WEAPON_TIMESTAMP = 4
101    CRAFTING_MEMENTOS = 5
102    CRAFTING_MEMENTO_TITLE = 6

An enumeration.

NONE = <ObjectiveUIStyle.NONE: 0>
HIGHLIGHTED = <ObjectiveUIStyle.HIGHLIGHTED: 1>
CRAFTING_WEAPON_LEVEL = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL: 2>
CRAFTING_WEAPON_LEVEL_PROGRESS = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL_PROGRESS: 3>
CRAFTING_WEAPON_TIMESTAMP = <ObjectiveUIStyle.CRAFTING_WEAPON_TIMESTAMP: 4>
CRAFTING_MEMENTOS = <ObjectiveUIStyle.CRAFTING_MEMENTOS: 5>
CRAFTING_MEMENTO_TITLE = <ObjectiveUIStyle.CRAFTING_MEMENTO_TITLE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Place(builtins.int, aiobungie.Enum):
230@typing.final
231class Place(int, Enum):
232    """An Enum for Destiny 2 Places and NOT Planets"""
233
234    ORBIT = 2961497387
235    SOCIAL = 4151112093
236    LIGHT_HOUSE = 4276116472
237    EXPLORE = 3497767639

An Enum for Destiny 2 Places and NOT Planets

ORBIT = <Place.ORBIT: 2961497387>
SOCIAL = <Place.SOCIAL: 4151112093>
LIGHT_HOUSE = <Place.LIGHT_HOUSE: 4276116472>
EXPLORE = <Place.EXPLORE: 3497767639>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Planet(builtins.int, aiobungie.Enum):
195@typing.final
196class Planet(int, Enum):
197    """An Enum for all available planets in Destiny 2."""
198
199    UNKNOWN = 0
200    """Unknown space"""
201
202    EARTH = 3747705955
203    """Earth"""
204
205    DREAMING_CITY = 2877881518
206    """The Dreaming city."""
207
208    NESSUS = 3526908984
209    """Nessus"""
210
211    MOON = 3325508439
212    """The Moon"""
213
214    COSMODROME = 3990611421
215    """The Cosmodrome"""
216
217    TANGLED_SHORE = 3821439926
218    """The Tangled Shore"""
219
220    VENUS = 3871070152
221    """Venus"""
222
223    EAZ = 541863059  # Exclusive event.
224    """European Aerial Zone"""
225
226    EUROPA = 1729879943
227    """Europa"""

An Enum for all available planets in Destiny 2.

UNKNOWN = <Planet.UNKNOWN: 0>

Unknown space

EARTH = <Planet.EARTH: 3747705955>

Earth

DREAMING_CITY = <Planet.DREAMING_CITY: 2877881518>

The Dreaming city.

NESSUS = <Planet.NESSUS: 3526908984>

Nessus

MOON = <Planet.MOON: 3325508439>

The Moon

COSMODROME = <Planet.COSMODROME: 3990611421>

The Cosmodrome

TANGLED_SHORE = <Planet.TANGLED_SHORE: 3821439926>

The Tangled Shore

VENUS = <Planet.VENUS: 3871070152>

Venus

EAZ = <Planet.EAZ: 541863059>

European Aerial Zone

EUROPA = <Planet.EUROPA: 1729879943>

Europa

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Presence(builtins.int, aiobungie.Enum):
680@typing.final
681class Presence(int, Enum):
682    """An enum for a bungie friend status."""
683
684    OFFLINE_OR_UNKNOWN = 0
685    ONLINE = 1

An enum for a bungie friend status.

OFFLINE_OR_UNKNOWN = <Presence.OFFLINE_OR_UNKNOWN: 0>
ONLINE = <Presence.ONLINE: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class PrivacySetting(builtins.int, aiobungie.Enum):
768@typing.final
769class PrivacySetting(int, Enum):
770    """An enum for players's privacy settings."""
771
772    OPEN = 0
773    CLAN_AND_FRIENDS = 1
774    FRIENDS_ONLY = 2
775    INVITE_ONLY = 3
776    CLOSED = 4

An enum for players's privacy settings.

OPEN = <PrivacySetting.OPEN: 0>
CLAN_AND_FRIENDS = <PrivacySetting.CLAN_AND_FRIENDS: 1>
FRIENDS_ONLY = <PrivacySetting.FRIENDS_ONLY: 2>
INVITE_ONLY = <PrivacySetting.INVITE_ONLY: 3>
CLOSED = <PrivacySetting.CLOSED: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RESTClient(aiobungie.interfaces.rest.RESTInterface):
 356class RESTClient(interfaces.RESTInterface):
 357    """A RESTful client implementation for Bungie's API.
 358
 359    This client is designed to only make HTTP requests and return JSON objects
 360    to provide RESTful functionality.
 361
 362    This client is also used within `aiobungie.Client` which deserialize those returned JSON objects
 363    using the factory into Pythonic data classes objects which provide Python functionality.
 364
 365    Example
 366    -------
 367    ```py
 368    import aiobungie
 369
 370    async def main():
 371        async with aiobungie.RESTClient("TOKEN") as rest_client:
 372            req = await rest_client.fetch_clan_members(4389205)
 373            clan_members = req['results']
 374            for member in clan_members:
 375                for k, v in member['destinyUserInfo'].items():
 376                    print(k, v)
 377    ```
 378
 379    Parameters
 380    ----------
 381    token : `str`
 382        A valid application token from Bungie's developer portal.
 383
 384    Other Parameters
 385    ----------------
 386    max_retries : `int`
 387        The max retries number to retry if the request hit a `5xx` status code.
 388    max_ratelimit_retries : `int`
 389        The max retries number to retry if the request hit a `429` status code. Defaults to `3`.
 390    client_secret : `typing.Optional[str]`
 391        An optional application client secret,
 392        This is only needed if you're fetching OAuth2 tokens with this client.
 393    client_id : `typing.Optional[int]`
 394        An optional application client id,
 395        This is only needed if you're fetching OAuth2 tokens with this client.
 396    enable_debugging : `bool | str`
 397        Whether to enable logging responses or not.
 398
 399    Logging Levels
 400    --------------
 401    * `False`: This will disable logging.
 402    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
 403    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
 404    """
 405
 406    __slots__ = (
 407        "_token",
 408        "_session",
 409        "_lock",
 410        "_max_retries",
 411        "_client_secret",
 412        "_client_id",
 413        "_metadata",
 414        "_max_rate_limit_retries",
 415    )
 416
 417    def __init__(
 418        self,
 419        token: str,
 420        /,
 421        client_secret: typing.Optional[str] = None,
 422        client_id: typing.Optional[int] = None,
 423        *,
 424        max_retries: int = 4,
 425        max_ratelimit_retries: int = 3,
 426        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 427    ) -> None:
 428        self._session: typing.Optional[_Session] = None
 429        self._lock: typing.Optional[asyncio.Lock] = None
 430        self._client_secret = client_secret
 431        self._client_id = client_id
 432        self._token: str = token
 433        self._max_retries = max_retries
 434        self._max_rate_limit_retries = max_ratelimit_retries
 435        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
 436
 437        self._set_debug_level(enable_debugging)
 438
 439    @property
 440    def client_id(self) -> typing.Optional[int]:
 441        return self._client_id
 442
 443    @property
 444    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 445        return self._metadata
 446
 447    @property
 448    def is_alive(self) -> bool:
 449        return self._session is not None
 450
 451    @typing.final
 452    async def close(self) -> None:
 453        session = self._get_session()
 454        await session.close()
 455        self._session = None
 456
 457    @typing.final
 458    def open(self) -> None:
 459        """Open a new client session. This is called internally with contextmanager usage."""
 460        if self.is_alive:
 461            raise RuntimeError("Cannot open a new session while it's already open.")
 462
 463        self._session = _Session.create()
 464
 465    @typing.final
 466    def enable_debugging(
 467        self,
 468        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 469        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
 470        /,
 471    ) -> None:
 472        self._set_debug_level(level, file)
 473
 474    @typing.final
 475    async def static_request(
 476        self,
 477        method: typing.Union[RequestMethod, str],
 478        path: str,
 479        *,
 480        auth: typing.Optional[str] = None,
 481        json: typing.Optional[dict[str, typing.Any]] = None,
 482    ) -> ResponseSig:
 483        return await self._request(method, path, auth=auth, json=json)
 484
 485    @typing.final
 486    def build_oauth2_url(
 487        self, client_id: typing.Optional[int] = None
 488    ) -> typing.Optional[builders.OAuthURL]:
 489        client_id = client_id or self._client_id
 490        if client_id is None:
 491            return None
 492
 493        return builders.OAuthURL(client_id=client_id)
 494
 495    @staticmethod
 496    def _set_debug_level(
 497        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 498        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
 499    ) -> None:
 500
 501        file_handler = logging.FileHandler(file, mode="w") if file else None
 502        if level == "TRACE" or level == TRACE:
 503            logging.basicConfig(
 504                level=TRACE, handlers=[file_handler] if file_handler else None
 505            )
 506
 507        elif level:
 508            logging.basicConfig(
 509                level=logging.DEBUG, handlers=[file_handler] if file_handler else None
 510            )
 511
 512    def _get_session(self) -> _Session:
 513        if self._session:
 514            return self._session
 515
 516        raise RuntimeError(
 517            "Cannot return a session while its close. Make sure you use `async with` before making requests."
 518        )
 519
 520    async def _request(
 521        self,
 522        method: typing.Union[RequestMethod, str],
 523        route: str,
 524        *,
 525        base: bool = False,
 526        oauth2: bool = False,
 527        auth: typing.Optional[str] = None,
 528        unwrapping: typing.Literal["json", "read"] = "json",
 529        json: typing.Optional[dict[str, typing.Any]] = None,
 530        headers: typing.Optional[dict[str, typing.Any]] = None,
 531        data: typing.Optional[typing.Union[str, dict[str, typing.Any]]] = None,
 532    ) -> ResponseSig:
 533
 534        retries: int = 0
 535        session = self._get_session()
 536        headers = headers or {}
 537
 538        headers.setdefault(_USER_AGENT_HEADERS, _USER_AGENT)
 539        headers["X-API-KEY"] = self._token
 540
 541        if auth is not None:
 542            headers[_AUTH_HEADER] = f"Bearer {auth}"
 543
 544        # Handling endpoints
 545        endpoint = url.BASE
 546
 547        if not base:
 548            endpoint = endpoint + url.REST_EP
 549
 550        if oauth2:
 551            headers["Content-Type"] = "application/x-www-form-urlencoded"
 552            endpoint = endpoint + url.TOKEN_EP
 553
 554        if self._lock is None:
 555            self._lock = asyncio.Lock()
 556
 557        while True:
 558            try:
 559                async with (stack := contextlib.AsyncExitStack()):
 560                    await stack.enter_async_context(self._lock)
 561
 562                    # We make the request here.
 563                    taken_time = time.monotonic()
 564                    response = await stack.enter_async_context(
 565                        session.client_session.request(
 566                            method=method,
 567                            url=f"{endpoint}/{route}",
 568                            json=json,
 569                            headers=headers,
 570                            data=data,
 571                        )
 572                    )
 573                    response_time = (time.monotonic() - taken_time) * 1_000
 574
 575                    _LOG.debug(
 576                        "%s %s %s Time %.4fms",
 577                        method,
 578                        f"{endpoint}/{route}",
 579                        f"{response.status} {response.reason}",
 580                        response_time,
 581                    )
 582
 583                    await self._handle_ratelimit(
 584                        response, method, route, self._max_rate_limit_retries
 585                    )
 586
 587                    if response.status == http.HTTPStatus.NO_CONTENT:
 588                        return None
 589
 590                    if 300 > response.status >= 200:
 591                        if unwrapping == "read":
 592                            # We need to read the bytes for the manifest response.
 593                            return await response.read()
 594
 595                        if response.content_type == _APP_JSON:
 596                            json_data = await response.json()
 597
 598                            _LOG.debug(
 599                                "%s %s %s Time %.4fms",
 600                                method,
 601                                f"{endpoint}/{route}",
 602                                f"{response.status} {response.reason}",
 603                                response_time,
 604                            )
 605
 606                            if _LOG.isEnabledFor(TRACE):
 607                                headers.update(response.headers)  # type: ignore
 608
 609                                _LOG.log(
 610                                    TRACE,
 611                                    "%s",
 612                                    error.stringify_http_message(headers),
 613                                )
 614
 615                            # Return the response.
 616                            # oauth2 responses are not packed inside a Response object.
 617                            if oauth2:
 618                                return json_data  # type: ignore[no-any-return]
 619
 620                            return json_data["Response"]  # type: ignore[no-any-return]
 621
 622                    if (
 623                        response.status in _RETRY_5XX
 624                        and retries < self._max_retries  # noqa: W503
 625                    ):
 626                        backoff_ = backoff.ExponentialBackOff(maximum=6)
 627                        sleep_time = next(backoff_)
 628                        _LOG.warning(
 629                            "Got %i - %s. Sleeping for %.2f seconds. Remaining retries: %i",
 630                            response.status,
 631                            response.reason,
 632                            sleep_time,
 633                            self._max_retries - retries,
 634                        )
 635
 636                        retries += 1
 637                        await asyncio.sleep(sleep_time)
 638                        continue
 639
 640                    raise await error.raise_error(response)
 641            # eol
 642            except _Dyn:
 643                continue
 644
 645    if not typing.TYPE_CHECKING:
 646
 647        def __enter__(self) -> typing.NoReturn:
 648            cls = type(self)
 649            raise TypeError(
 650                f"{cls.__qualname__} is async only, use 'async with' instead."
 651            )
 652
 653        def __exit__(
 654            self,
 655            exception_type: typing.Optional[type[BaseException]],
 656            exception: typing.Optional[BaseException],
 657            exception_traceback: typing.Optional[types.TracebackType],
 658        ) -> None:
 659            ...
 660
 661    async def __aenter__(self) -> RESTClient:
 662        self.open()
 663        return self
 664
 665    async def __aexit__(
 666        self,
 667        exception_type: typing.Optional[type[BaseException]],
 668        exception: typing.Optional[BaseException],
 669        exception_traceback: typing.Optional[types.TracebackType],
 670    ) -> None:
 671        await self.close()
 672
 673    # We don't want this to be super complicated.
 674    @staticmethod
 675    @typing.final
 676    async def _handle_ratelimit(
 677        response: aiohttp.ClientResponse,
 678        method: str,
 679        route: str,
 680        max_ratelimit_retries: int = 3,
 681    ) -> None:
 682
 683        if response.status != http.HTTPStatus.TOO_MANY_REQUESTS:
 684            return
 685
 686        if response.content_type != _APP_JSON:
 687            raise error.HTTPError(
 688                f"Being ratelimited on non JSON request, {response.content_type}.",
 689                http.HTTPStatus.TOO_MANY_REQUESTS,
 690            )
 691
 692        count: int = 0
 693        json: typedefs.JSONObject = await response.json()
 694        retry_after = float(json["ThrottleSeconds"])
 695
 696        while True:
 697            if count == max_ratelimit_retries:
 698                raise _Dyn
 699
 700            if retry_after <= 0:
 701                # We sleep for a little bit to avoid funky behavior.
 702                sleep_time = float(random.random() + 0.93) / 2
 703
 704                _LOG.warning(
 705                    "We're being ratelimited with method %s route %s. Sleeping for %.2fs.",
 706                    method,
 707                    route,
 708                    sleep_time,
 709                )
 710                count += 1
 711                await asyncio.sleep(sleep_time)
 712                continue
 713
 714            raise error.RateLimitedError(
 715                body=json,
 716                url=str(response.real_url),
 717                retry_after=retry_after,
 718            )
 719
 720    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
 721
 722        if not isinstance(self._client_id, int):
 723            raise TypeError(
 724                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
 725            )
 726
 727        if not isinstance(self._client_secret, str):
 728            raise TypeError(
 729                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
 730            )
 731
 732        headers = {
 733            "client_secret": self._client_secret,
 734        }
 735
 736        data = (
 737            f"grant_type=authorization_code&code={code}"
 738            f"&client_id={self._client_id}&client_secret={self._client_secret}"
 739        )
 740
 741        response = await self._request(
 742            RequestMethod.POST, "", headers=headers, data=data, oauth2=True
 743        )
 744        assert isinstance(response, dict)
 745        return builders.OAuth2Response.build_response(response)
 746
 747    async def refresh_access_token(
 748        self, refresh_token: str, /
 749    ) -> builders.OAuth2Response:
 750        if not isinstance(self._client_id, int):
 751            raise TypeError(
 752                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
 753            )
 754
 755        if not isinstance(self._client_secret, str):
 756            raise TypeError(
 757                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
 758            )
 759
 760        data = {
 761            "grant_type": "refresh_token",
 762            "refresh_token": refresh_token,
 763            "client_id": self._client_id,
 764            "client_secret": self._client_secret,
 765            "Content-Type": "application/x-www-form-urlencoded",
 766        }
 767
 768        response = await self._request(RequestMethod.POST, "", data=data, oauth2=True)
 769        assert isinstance(response, dict)
 770        return builders.OAuth2Response.build_response(response)
 771
 772    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
 773        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 774        resp = await self._request(
 775            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
 776        )
 777        assert isinstance(resp, dict)
 778        return resp
 779
 780    async def fetch_user_themes(self) -> typedefs.JSONArray:
 781        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 782        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
 783        assert isinstance(resp, list)
 784        return resp
 785
 786    async def fetch_membership_from_id(
 787        self,
 788        id: int,
 789        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 790        /,
 791    ) -> typedefs.JSONObject:
 792        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 793        resp = await self._request(
 794            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
 795        )
 796        assert isinstance(resp, dict)
 797        return resp
 798
 799    async def fetch_player(
 800        self,
 801        name: str,
 802        code: int,
 803        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
 804        /,
 805    ) -> typedefs.JSONArray:
 806        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 807        resp = await self._request(
 808            RequestMethod.POST,
 809            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
 810            json={"displayName": name, "displayNameCode": code},
 811        )
 812        assert isinstance(resp, list)
 813        return resp
 814
 815    async def search_users(self, name: str, /) -> typedefs.JSONObject:
 816        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 817        resp = await self._request(
 818            RequestMethod.POST,
 819            "User/Search/GlobalName/0",
 820            json={"displayNamePrefix": name},
 821        )
 822        assert isinstance(resp, dict)
 823        return resp
 824
 825    async def fetch_clan_from_id(
 826        self, id: int, /, access_token: typing.Optional[str] = None
 827    ) -> typedefs.JSONObject:
 828        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 829        resp = await self._request(
 830            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
 831        )
 832        assert isinstance(resp, dict)
 833        return resp
 834
 835    async def fetch_clan(
 836        self,
 837        name: str,
 838        /,
 839        access_token: typing.Optional[str] = None,
 840        *,
 841        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 842    ) -> typedefs.JSONObject:
 843        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 844        resp = await self._request(
 845            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
 846        )
 847        assert isinstance(resp, dict)
 848        return resp
 849
 850    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
 851        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 852        resp = await self._request(
 853            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
 854        )
 855        assert isinstance(resp, dict)
 856        return resp
 857
 858    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
 859        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 860        resp = await self._request(
 861            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
 862        )
 863        assert isinstance(resp, list)
 864        return resp
 865
 866    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
 867        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 868        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
 869        assert isinstance(resp, dict)
 870        return resp
 871
 872    async def fetch_character(
 873        self,
 874        member_id: int,
 875        membership_type: typedefs.IntAnd[enums.MembershipType],
 876        character_id: int,
 877        components: list[enums.ComponentType],
 878        auth: typing.Optional[str] = None,
 879    ) -> typedefs.JSONObject:
 880        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 881        collector = _collect_components(components)
 882        response = await self._request(
 883            RequestMethod.GET,
 884            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
 885            f"Character/{character_id}/?components={collector}",
 886            auth=auth,
 887        )
 888        assert isinstance(response, dict)
 889        return response
 890
 891    async def fetch_activities(
 892        self,
 893        member_id: int,
 894        character_id: int,
 895        mode: typedefs.IntAnd[enums.GameMode],
 896        membership_type: typedefs.IntAnd[
 897            enums.MembershipType
 898        ] = enums.MembershipType.ALL,
 899        *,
 900        page: int = 0,
 901        limit: int = 1,
 902    ) -> typedefs.JSONObject:
 903        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 904        resp = await self._request(
 905            RequestMethod.GET,
 906            f"Destiny2/{int(membership_type)}/Account/"
 907            f"{member_id}/Character/{character_id}/Stats/Activities"
 908            f"/?mode={int(mode)}&count={limit}&page={page}",
 909        )
 910        assert isinstance(resp, dict)
 911        return resp
 912
 913    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
 914        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 915        resp = await self._request(
 916            RequestMethod.GET,
 917            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
 918        )
 919        assert isinstance(resp, dict)
 920        return resp
 921
 922    async def fetch_profile(
 923        self,
 924        membership_id: int,
 925        type: typedefs.IntAnd[enums.MembershipType],
 926        components: list[enums.ComponentType],
 927        auth: typing.Optional[str] = None,
 928    ) -> typedefs.JSONObject:
 929        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 930        collector = _collect_components(components)
 931        response = await self._request(
 932            RequestMethod.GET,
 933            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
 934            auth=auth,
 935        )
 936        assert isinstance(response, dict)
 937        return response
 938
 939    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
 940        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 941        response = await self._request(
 942            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
 943        )
 944        assert isinstance(response, dict)
 945        return response
 946
 947    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
 948        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 949        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
 950        assert isinstance(resp, dict)
 951        return resp
 952
 953    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
 954        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 955        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
 956        assert isinstance(resp, dict)
 957        return resp
 958
 959    async def fetch_groups_for_member(
 960        self,
 961        member_id: int,
 962        member_type: typedefs.IntAnd[enums.MembershipType],
 963        /,
 964        *,
 965        filter: int = 0,
 966        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 967    ) -> typedefs.JSONObject:
 968        resp = await self._request(
 969            RequestMethod.GET,
 970            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 971        )
 972        assert isinstance(resp, dict)
 973        return resp
 974
 975    async def fetch_potential_groups_for_member(
 976        self,
 977        member_id: int,
 978        member_type: typedefs.IntAnd[enums.MembershipType],
 979        /,
 980        *,
 981        filter: int = 0,
 982        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 983    ) -> typedefs.JSONObject:
 984        resp = await self._request(
 985            RequestMethod.GET,
 986            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 987        )
 988        assert isinstance(resp, dict)
 989        return resp
 990
 991    async def fetch_clan_members(
 992        self,
 993        clan_id: int,
 994        /,
 995        *,
 996        name: typing.Optional[str] = None,
 997        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 998    ) -> typedefs.JSONObject:
 999        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1000        resp = await self._request(
1001            RequestMethod.GET,
1002            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
1003        )
1004        assert isinstance(resp, dict)
1005        return resp
1006
1007    async def fetch_hardlinked_credentials(
1008        self,
1009        credential: int,
1010        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
1011        /,
1012    ) -> typedefs.JSONObject:
1013        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1014        resp = await self._request(
1015            RequestMethod.GET,
1016            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
1017        )
1018        assert isinstance(resp, dict)
1019        return resp
1020
1021    async def fetch_user_credentials(
1022        self, access_token: str, membership_id: int, /
1023    ) -> typedefs.JSONArray:
1024        resp = await self._request(
1025            RequestMethod.GET,
1026            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
1027            auth=access_token,
1028        )
1029        assert isinstance(resp, list)
1030        return resp
1031
1032    async def insert_socket_plug(
1033        self,
1034        action_token: str,
1035        /,
1036        instance_id: int,
1037        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1038        character_id: int,
1039        membership_type: typedefs.IntAnd[enums.MembershipType],
1040    ) -> typedefs.JSONObject:
1041
1042        if isinstance(plug, builders.PlugSocketBuilder):
1043            plug = plug.collect()
1044
1045        body = {
1046            "actionToken": action_token,
1047            "itemInstanceId": instance_id,
1048            "plug": plug,
1049            "characterId": character_id,
1050            "membershipType": int(membership_type),
1051        }
1052        resp = await self._request(
1053            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
1054        )
1055        assert isinstance(resp, dict)
1056        return resp
1057
1058    async def insert_socket_plug_free(
1059        self,
1060        access_token: str,
1061        /,
1062        instance_id: int,
1063        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1064        character_id: int,
1065        membership_type: typedefs.IntAnd[enums.MembershipType],
1066    ) -> typedefs.JSONObject:
1067
1068        if isinstance(plug, builders.PlugSocketBuilder):
1069            plug = plug.collect()
1070
1071        body = {
1072            "itemInstanceId": instance_id,
1073            "plug": plug,
1074            "characterId": character_id,
1075            "membershipType": int(membership_type),
1076        }
1077        resp = await self._request(
1078            RequestMethod.POST,
1079            "Destiny2/Actions/Items/InsertSocketPlugFree",
1080            json=body,
1081            auth=access_token,
1082        )
1083        assert isinstance(resp, dict)
1084        return resp
1085
1086    async def set_item_lock_state(
1087        self,
1088        access_token: str,
1089        state: bool,
1090        /,
1091        item_id: int,
1092        character_id: int,
1093        membership_type: typedefs.IntAnd[enums.MembershipType],
1094    ) -> int:
1095        body = {
1096            "state": state,
1097            "itemId": item_id,
1098            "characterId": character_id,
1099            "membership_type": int(membership_type),
1100        }
1101        response = await self._request(
1102            RequestMethod.POST,
1103            "Destiny2/Actions/Items/SetLockState",
1104            json=body,
1105            auth=access_token,
1106        )
1107        assert isinstance(response, int)
1108        return response
1109
1110    async def set_quest_track_state(
1111        self,
1112        access_token: str,
1113        state: bool,
1114        /,
1115        item_id: int,
1116        character_id: int,
1117        membership_type: typedefs.IntAnd[enums.MembershipType],
1118    ) -> int:
1119        body = {
1120            "state": state,
1121            "itemId": item_id,
1122            "characterId": character_id,
1123            "membership_type": int(membership_type),
1124        }
1125        response = await self._request(
1126            RequestMethod.POST,
1127            "Destiny2/Actions/Items/SetTrackedState",
1128            json=body,
1129            auth=access_token,
1130        )
1131        assert isinstance(response, int)
1132        return response
1133
1134    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1135        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1136        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1137        assert isinstance(path, dict)
1138        return path
1139
1140    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1141        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1142        _ensure_manifest_language(language)
1143
1144        content = await self.fetch_manifest_path()
1145        resp = await self._request(
1146            RequestMethod.GET,
1147            content["mobileWorldContentPaths"][language],
1148            unwrapping="read",
1149            base=True,
1150        )
1151        assert isinstance(resp, bytes)
1152        return resp
1153
1154    async def download_manifest(
1155        self,
1156        language: str = "en",
1157        name: str = "manifest",
1158        path: typing.Union[pathlib.Path, str] = ".",
1159        *,
1160        force: bool = False,
1161    ) -> None:
1162        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1163        complete_path = _get_path(name, path, sql=True)
1164
1165        if complete_path.exists() and force:
1166            if force:
1167                _LOG.info(
1168                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1169                )
1170                complete_path.unlink(missing_ok=True)
1171
1172                return await self.download_manifest(language, name, path, force=force)
1173
1174            else:
1175                raise FileExistsError(
1176                    "Manifest file already exists, "
1177                    "To force download, set the `force` parameter to `True`."
1178                )
1179
1180        _LOG.info(f"Downloading manifest. Location: {complete_path!s}")
1181        data_bytes = await self.read_manifest_bytes(language)
1182        await asyncio.get_running_loop().run_in_executor(
1183            None, _write_sqlite_bytes, data_bytes, path, name
1184        )
1185
1186    async def download_json_manifest(
1187        self,
1188        file_name: str = "manifest",
1189        path: typing.Union[str, pathlib.Path] = ".",
1190        language: str = "en",
1191    ) -> None:
1192        _ensure_manifest_language(language)
1193
1194        _LOG.info(f"Downloading manifest JSON to {_get_path(file_name, path)!r}...")
1195
1196        content = await self.fetch_manifest_path()
1197        json_bytes = await self._request(
1198            RequestMethod.GET,
1199            content["jsonWorldContentPaths"][language],
1200            unwrapping="read",
1201            base=True,
1202        )
1203
1204        await asyncio.get_running_loop().run_in_executor(
1205            None, _write_json_bytes, json_bytes, file_name, path
1206        )
1207        _LOG.info("Finished downloading manifest JSON.")
1208
1209    async def fetch_manifest_version(self) -> str:
1210        return typing.cast(str, (await self.fetch_manifest_path())["version"])
1211
1212    async def fetch_linked_profiles(
1213        self,
1214        member_id: int,
1215        member_type: typedefs.IntAnd[enums.MembershipType],
1216        /,
1217        *,
1218        all: bool = False,
1219    ) -> typedefs.JSONObject:
1220        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1221        resp = await self._request(
1222            RequestMethod.GET,
1223            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1224        )
1225        assert isinstance(resp, dict)
1226        return resp
1227
1228    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1229        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1230        resp = await self._request(
1231            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1232        )
1233        assert isinstance(resp, dict)
1234        return resp
1235
1236    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1237        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1238        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1239        assert isinstance(resp, dict)
1240        return resp
1241
1242    async def fetch_public_milestone_content(
1243        self, milestone_hash: int, /
1244    ) -> typedefs.JSONObject:
1245        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1246        resp = await self._request(
1247            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1248        )
1249        assert isinstance(resp, dict)
1250        return resp
1251
1252    async def fetch_current_user_memberships(
1253        self, access_token: str, /
1254    ) -> typedefs.JSONObject:
1255        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1256        resp = await self._request(
1257            RequestMethod.GET,
1258            "User/GetMembershipsForCurrentUser/",
1259            auth=access_token,
1260        )
1261        assert isinstance(resp, dict)
1262        return resp
1263
1264    async def equip_item(
1265        self,
1266        access_token: str,
1267        /,
1268        item_id: int,
1269        character_id: int,
1270        membership_type: typedefs.IntAnd[enums.MembershipType],
1271    ) -> None:
1272        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1273        payload = {
1274            "itemId": item_id,
1275            "characterId": character_id,
1276            "membershipType": int(membership_type),
1277        }
1278
1279        await self._request(
1280            RequestMethod.POST,
1281            "Destiny2/Actions/Items/EquipItem/",
1282            json=payload,
1283            auth=access_token,
1284        )
1285
1286    async def equip_items(
1287        self,
1288        access_token: str,
1289        /,
1290        item_ids: list[int],
1291        character_id: int,
1292        membership_type: typedefs.IntAnd[enums.MembershipType],
1293    ) -> None:
1294        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1295        payload = {
1296            "itemIds": item_ids,
1297            "characterId": character_id,
1298            "membershipType": int(membership_type),
1299        }
1300        await self._request(
1301            RequestMethod.POST,
1302            "Destiny2/Actions/Items/EquipItems/",
1303            json=payload,
1304            auth=access_token,
1305        )
1306
1307    async def ban_clan_member(
1308        self,
1309        access_token: str,
1310        /,
1311        group_id: int,
1312        membership_id: int,
1313        membership_type: typedefs.IntAnd[enums.MembershipType],
1314        *,
1315        length: int = 0,
1316        comment: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1317    ) -> None:
1318        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1319        payload = {"comment": str(comment), "length": length}
1320        await self._request(
1321            RequestMethod.POST,
1322            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1323            json=payload,
1324            auth=access_token,
1325        )
1326
1327    async def unban_clan_member(
1328        self,
1329        access_token: str,
1330        /,
1331        group_id: int,
1332        membership_id: int,
1333        membership_type: typedefs.IntAnd[enums.MembershipType],
1334    ) -> None:
1335        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1336        await self._request(
1337            RequestMethod.POST,
1338            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1339            auth=access_token,
1340        )
1341
1342    async def kick_clan_member(
1343        self,
1344        access_token: str,
1345        /,
1346        group_id: int,
1347        membership_id: int,
1348        membership_type: typedefs.IntAnd[enums.MembershipType],
1349    ) -> typedefs.JSONObject:
1350        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1351        resp = await self._request(
1352            RequestMethod.POST,
1353            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1354            auth=access_token,
1355        )
1356        assert isinstance(resp, dict)
1357        return resp
1358
1359    async def edit_clan(
1360        self,
1361        access_token: str,
1362        /,
1363        group_id: int,
1364        *,
1365        name: typedefs.NoneOr[str] = None,
1366        about: typedefs.NoneOr[str] = None,
1367        motto: typedefs.NoneOr[str] = None,
1368        theme: typedefs.NoneOr[str] = None,
1369        tags: typedefs.NoneOr[collections.Sequence[str]] = None,
1370        is_public: typedefs.NoneOr[bool] = None,
1371        locale: typedefs.NoneOr[str] = None,
1372        avatar_image_index: typedefs.NoneOr[int] = None,
1373        membership_option: typedefs.NoneOr[
1374            typedefs.IntAnd[enums.MembershipOption]
1375        ] = None,
1376        allow_chat: typedefs.NoneOr[bool] = None,
1377        chat_security: typedefs.NoneOr[typing.Literal[0, 1]] = None,
1378        call_sign: typedefs.NoneOr[str] = None,
1379        homepage: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1380        enable_invite_messaging_for_admins: typedefs.NoneOr[bool] = None,
1381        default_publicity: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1382        is_public_topic_admin: typedefs.NoneOr[bool] = None,
1383    ) -> None:
1384        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1385        payload = {
1386            "name": name,
1387            "about": about,
1388            "motto": motto,
1389            "theme": theme,
1390            "tags": tags,
1391            "isPublic": is_public,
1392            "avatarImageIndex": avatar_image_index,
1393            "isPublicTopicAdminOnly": is_public_topic_admin,
1394            "allowChat": allow_chat,
1395            "chatSecurity": chat_security,
1396            "callsign": call_sign,
1397            "homepage": homepage,
1398            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1399            "defaultPublicity": default_publicity,
1400            "locale": locale,
1401        }
1402        if membership_option is not None:
1403            payload["membershipOption"] = int(membership_option)
1404
1405        await self._request(
1406            RequestMethod.POST,
1407            f"GroupV2/{group_id}/Edit",
1408            json=payload,
1409            auth=access_token,
1410        )
1411
1412    async def edit_clan_options(
1413        self,
1414        access_token: str,
1415        /,
1416        group_id: int,
1417        *,
1418        invite_permissions_override: typedefs.NoneOr[bool] = None,
1419        update_culture_permissionOverride: typedefs.NoneOr[bool] = None,
1420        host_guided_game_permission_override: typedefs.NoneOr[
1421            typing.Literal[0, 1, 2]
1422        ] = None,
1423        update_banner_permission_override: typedefs.NoneOr[bool] = None,
1424        join_level: typedefs.NoneOr[typedefs.IntAnd[enums.ClanMemberType]] = None,
1425    ) -> None:
1426
1427        payload = {
1428            "InvitePermissionOverride": invite_permissions_override,
1429            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1430            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1431            "UpdateBannerPermissionOverride": update_banner_permission_override,
1432            "JoinLevel": int(join_level) if join_level else None,
1433        }
1434
1435        await self._request(
1436            RequestMethod.POST,
1437            f"GroupV2/{group_id}/EditFounderOptions",
1438            json=payload,
1439            auth=access_token,
1440        )
1441
1442    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1443        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1444        resp = await self._request(
1445            RequestMethod.GET,
1446            "Social/Friends/",
1447            auth=access_token,
1448        )
1449        assert isinstance(resp, dict)
1450        return resp
1451
1452    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1453        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1454        resp = await self._request(
1455            RequestMethod.GET,
1456            "Social/Friends/Requests",
1457            auth=access_token,
1458        )
1459        assert isinstance(resp, dict)
1460        return resp
1461
1462    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1463        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1464        await self._request(
1465            RequestMethod.POST,
1466            f"Social/Friends/Requests/Accept/{member_id}",
1467            auth=access_token,
1468        )
1469
1470    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1471        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1472        await self._request(
1473            RequestMethod.POST,
1474            f"Social/Friends/Add/{member_id}",
1475            auth=access_token,
1476        )
1477
1478    async def decline_friend_request(
1479        self, access_token: str, /, member_id: int
1480    ) -> None:
1481        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1482        await self._request(
1483            RequestMethod.POST,
1484            f"Social/Friends/Requests/Decline/{member_id}",
1485            auth=access_token,
1486        )
1487
1488    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1489        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1490        await self._request(
1491            RequestMethod.POST,
1492            f"Social/Friends/Remove/{member_id}",
1493            auth=access_token,
1494        )
1495
1496    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1497        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1498        await self._request(
1499            RequestMethod.POST,
1500            f"Social/Friends/Requests/Remove/{member_id}",
1501            auth=access_token,
1502        )
1503
1504    async def approve_all_pending_group_users(
1505        self,
1506        access_token: str,
1507        /,
1508        group_id: int,
1509        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1510    ) -> None:
1511        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1512        await self._request(
1513            RequestMethod.POST,
1514            f"GroupV2/{group_id}/Members/ApproveAll",
1515            auth=access_token,
1516            json={"message": str(message)},
1517        )
1518
1519    async def deny_all_pending_group_users(
1520        self,
1521        access_token: str,
1522        /,
1523        group_id: int,
1524        *,
1525        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1526    ) -> None:
1527        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1528        await self._request(
1529            RequestMethod.POST,
1530            f"GroupV2/{group_id}/Members/DenyAll",
1531            auth=access_token,
1532            json={"message": str(message)},
1533        )
1534
1535    async def add_optional_conversation(
1536        self,
1537        access_token: str,
1538        /,
1539        group_id: int,
1540        *,
1541        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1542        security: typing.Literal[0, 1] = 0,
1543    ) -> None:
1544        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1545        payload = {"chatName": str(name), "chatSecurity": security}
1546        await self._request(
1547            RequestMethod.POST,
1548            f"GroupV2/{group_id}/OptionalConversations/Add",
1549            json=payload,
1550            auth=access_token,
1551        )
1552
1553    async def edit_optional_conversation(
1554        self,
1555        access_token: str,
1556        /,
1557        group_id: int,
1558        conversation_id: int,
1559        *,
1560        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1561        security: typing.Literal[0, 1] = 0,
1562        enable_chat: bool = False,
1563    ) -> None:
1564        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1565        payload = {
1566            "chatEnabled": enable_chat,
1567            "chatName": str(name),
1568            "chatSecurity": security,
1569        }
1570        await self._request(
1571            RequestMethod.POST,
1572            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1573            json=payload,
1574            auth=access_token,
1575        )
1576
1577    async def transfer_item(
1578        self,
1579        access_token: str,
1580        /,
1581        item_id: int,
1582        item_hash: int,
1583        character_id: int,
1584        member_type: typedefs.IntAnd[enums.MembershipType],
1585        *,
1586        stack_size: int = 1,
1587        vault: bool = False,
1588    ) -> None:
1589        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1590        payload = {
1591            "characterId": character_id,
1592            "membershipType": int(member_type),
1593            "itemId": item_id,
1594            "itemReferenceHash": item_hash,
1595            "stackSize": stack_size,
1596            "transferToVault": vault,
1597        }
1598        await self._request(
1599            RequestMethod.POST,
1600            "Destiny2/Actions/Items/TransferItem",
1601            json=payload,
1602            auth=access_token,
1603        )
1604
1605    async def pull_item(
1606        self,
1607        access_token: str,
1608        /,
1609        item_id: int,
1610        item_hash: int,
1611        character_id: int,
1612        member_type: typedefs.IntAnd[enums.MembershipType],
1613        *,
1614        stack_size: int = 1,
1615        vault: bool = False,
1616    ) -> None:
1617        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1618        payload = {
1619            "characterId": character_id,
1620            "membershipType": int(member_type),
1621            "itemId": item_id,
1622            "itemReferenceHash": item_hash,
1623            "stackSize": stack_size,
1624            "transferToVault": vault,
1625        }
1626        await self._request(
1627            RequestMethod.POST,
1628            "Destiny2/Actions/Items/PullFromPostmaster",
1629            json=payload,
1630            auth=access_token,
1631        )
1632
1633    async def fetch_fireteams(
1634        self,
1635        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1636        *,
1637        platform: typedefs.IntAnd[
1638            fireteams.FireteamPlatform
1639        ] = fireteams.FireteamPlatform.ANY,
1640        language: typing.Union[
1641            fireteams.FireteamLanguage, str
1642        ] = fireteams.FireteamLanguage.ALL,
1643        date_range: typedefs.IntAnd[
1644            fireteams.FireteamDate
1645        ] = fireteams.FireteamDate.ALL,
1646        page: int = 0,
1647        slots_filter: int = 0,
1648    ) -> typedefs.JSONObject:
1649        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1650        resp = await self._request(
1651            RequestMethod.GET,
1652            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1653        )
1654        assert isinstance(resp, dict)
1655        return resp
1656
1657    async def fetch_avaliable_clan_fireteams(
1658        self,
1659        access_token: str,
1660        group_id: int,
1661        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1662        *,
1663        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1664        language: typing.Union[fireteams.FireteamLanguage, str],
1665        date_range: typedefs.IntAnd[
1666            fireteams.FireteamDate
1667        ] = fireteams.FireteamDate.ALL,
1668        page: int = 0,
1669        public_only: bool = False,
1670        slots_filter: int = 0,
1671    ) -> typedefs.JSONObject:
1672        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1673        resp = await self._request(
1674            RequestMethod.GET,
1675            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1676            json={"langFilter": str(language)},
1677            auth=access_token,
1678        )
1679        assert isinstance(resp, dict)
1680        return resp
1681
1682    async def fetch_clan_fireteam(
1683        self, access_token: str, fireteam_id: int, group_id: int
1684    ) -> typedefs.JSONObject:
1685        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1686        resp = await self._request(
1687            RequestMethod.GET,
1688            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1689            auth=access_token,
1690        )
1691        assert isinstance(resp, dict)
1692        return resp
1693
1694    async def fetch_my_clan_fireteams(
1695        self,
1696        access_token: str,
1697        group_id: int,
1698        *,
1699        include_closed: bool = True,
1700        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1701        language: typing.Union[fireteams.FireteamLanguage, str],
1702        filtered: bool = True,
1703        page: int = 0,
1704    ) -> typedefs.JSONObject:
1705        payload = {"groupFilter": filtered, "langFilter": str(language)}
1706        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1707        resp = await self._request(
1708            RequestMethod.GET,
1709            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1710            json=payload,
1711            auth=access_token,
1712        )
1713        assert isinstance(resp, dict)
1714        return resp
1715
1716    async def fetch_private_clan_fireteams(
1717        self, access_token: str, group_id: int, /
1718    ) -> int:
1719        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1720        resp = await self._request(
1721            RequestMethod.GET,
1722            f"Fireteam/Clan/{group_id}/ActiveCount",
1723            auth=access_token,
1724        )
1725        assert isinstance(resp, int)
1726        return resp
1727
1728    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1729        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1730        resp = await self._request(
1731            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1732        )
1733        assert isinstance(resp, dict)
1734        return resp
1735
1736    async def search_entities(
1737        self, name: str, entity_type: str, *, page: int = 0
1738    ) -> typedefs.JSONObject:
1739        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1740        resp = await self._request(
1741            RequestMethod.GET,
1742            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1743            json={"page": page},
1744        )
1745        assert isinstance(resp, dict)
1746        return resp
1747
1748    async def fetch_unique_weapon_history(
1749        self,
1750        membership_id: int,
1751        character_id: int,
1752        membership_type: typedefs.IntAnd[enums.MembershipType],
1753    ) -> typedefs.JSONObject:
1754        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1755        resp = await self._request(
1756            RequestMethod.GET,
1757            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1758        )
1759        assert isinstance(resp, dict)
1760        return resp
1761
1762    async def fetch_item(
1763        self,
1764        member_id: int,
1765        item_id: int,
1766        membership_type: typedefs.IntAnd[enums.MembershipType],
1767        components: list[enums.ComponentType],
1768    ) -> typedefs.JSONObject:
1769        collector = _collect_components(components)
1770        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1771        resp = await self._request(
1772            RequestMethod.GET,
1773            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1774        )
1775        assert isinstance(resp, dict)
1776        return resp
1777
1778    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1779        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1780        resp = await self._request(
1781            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1782        )
1783        assert isinstance(resp, dict)
1784        return resp
1785
1786    async def fetch_available_locales(self) -> typedefs.JSONObject:
1787        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1788        resp = await self._request(
1789            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1790        )
1791        assert isinstance(resp, dict)
1792        return resp
1793
1794    async def fetch_common_settings(self) -> typedefs.JSONObject:
1795        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1796        resp = await self._request(RequestMethod.GET, "Settings")
1797        assert isinstance(resp, dict)
1798        return resp
1799
1800    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1801        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1802        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1803        assert isinstance(resp, dict)
1804        return resp
1805
1806    async def fetch_global_alerts(
1807        self, *, include_streaming: bool = False
1808    ) -> typedefs.JSONArray:
1809        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1810        resp = await self._request(
1811            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1812        )
1813        assert isinstance(resp, list)
1814        return resp
1815
1816    async def awainitialize_request(
1817        self,
1818        access_token: str,
1819        type: typing.Literal[0, 1],
1820        membership_type: typedefs.IntAnd[enums.MembershipType],
1821        /,
1822        *,
1823        affected_item_id: typing.Optional[int] = None,
1824        character_id: typing.Optional[int] = None,
1825    ) -> typedefs.JSONObject:
1826        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1827
1828        body = {"type": type, "membershipType": int(membership_type)}
1829
1830        if affected_item_id is not None:
1831            body["affectedItemId"] = affected_item_id
1832
1833        if character_id is not None:
1834            body["characterId"] = character_id
1835
1836        resp = await self._request(
1837            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1838        )
1839        assert isinstance(resp, dict)
1840        return resp
1841
1842    async def awaget_action_token(
1843        self, access_token: str, correlation_id: str, /
1844    ) -> typedefs.JSONObject:
1845        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1846        resp = await self._request(
1847            RequestMethod.POST,
1848            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1849            auth=access_token,
1850        )
1851        assert isinstance(resp, dict)
1852        return resp
1853
1854    async def awa_provide_authorization_result(
1855        self,
1856        access_token: str,
1857        selection: int,
1858        correlation_id: str,
1859        nonce: collections.MutableSequence[typing.Union[str, bytes]],
1860    ) -> int:
1861        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1862
1863        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1864
1865        resp = await self._request(
1866            RequestMethod.POST,
1867            "Destiny2/Awa/AwaProvideAuthorizationResult",
1868            json=body,
1869            auth=access_token,
1870        )
1871        assert isinstance(resp, int)
1872        return resp
1873
1874    async def fetch_vendors(
1875        self,
1876        access_token: str,
1877        character_id: int,
1878        membership_id: int,
1879        membership_type: typedefs.IntAnd[enums.MembershipType],
1880        /,
1881        components: list[enums.ComponentType],
1882        filter: typing.Optional[int] = None,
1883    ) -> typedefs.JSONObject:
1884        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1885        components_ = _collect_components(components)
1886        route = (
1887            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1888            f"/Character/{character_id}/Vendors/?components={components_}"
1889        )
1890
1891        if filter is not None:
1892            route = route + f"&filter={filter}"
1893
1894        resp = await self._request(
1895            RequestMethod.GET,
1896            route,
1897            auth=access_token,
1898        )
1899        assert isinstance(resp, dict)
1900        return resp
1901
1902    async def fetch_vendor(
1903        self,
1904        access_token: str,
1905        character_id: int,
1906        membership_id: int,
1907        membership_type: typedefs.IntAnd[enums.MembershipType],
1908        vendor_hash: int,
1909        /,
1910        components: list[enums.ComponentType],
1911    ) -> typedefs.JSONObject:
1912        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1913        components_ = _collect_components(components)
1914        resp = await self._request(
1915            RequestMethod.GET,
1916            (
1917                f"Platform/Destiny2/{int(membership_type)}/Profile/{membership_id}"
1918                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1919            ),
1920            auth=access_token,
1921        )
1922        assert isinstance(resp, dict)
1923        return resp
1924
1925    async def fetch_application_api_usage(
1926        self,
1927        access_token: str,
1928        application_id: int,
1929        /,
1930        *,
1931        start: typing.Optional[datetime.datetime] = None,
1932        end: typing.Optional[datetime.datetime] = None,
1933    ) -> typedefs.JSONObject:
1934
1935        end_date, start_date = time.parse_date_range(end, start)
1936        resp = await self._request(
1937            RequestMethod.GET,
1938            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1939            auth=access_token,
1940        )
1941        assert isinstance(resp, dict)
1942        return resp
1943
1944    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1945        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1946        assert isinstance(resp, list)
1947        return resp
1948
1949    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1950        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1951        assert isinstance(resp, dict)
1952        return resp
1953
1954    async def fetch_content_by_id(
1955        self, id: int, locale: str, /, *, head: bool = False
1956    ) -> typedefs.JSONObject:
1957        resp = await self._request(
1958            RequestMethod.GET,
1959            f"Content/GetContentById/{id}/{locale}/",
1960            json={"head": head},
1961        )
1962        assert isinstance(resp, dict)
1963        return resp
1964
1965    async def fetch_content_by_tag_and_type(
1966        self, locale: str, tag: str, type: str, *, head: bool = False
1967    ) -> typedefs.JSONObject:
1968        resp = await self._request(
1969            RequestMethod.GET,
1970            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1971            json={"head": head},
1972        )
1973        assert isinstance(resp, dict)
1974        return resp
1975
1976    async def search_content_with_text(
1977        self,
1978        locale: str,
1979        /,
1980        content_type: str,
1981        search_text: str,
1982        tag: str,
1983        *,
1984        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1985        source: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1986    ) -> typedefs.JSONObject:
1987
1988        body: typedefs.JSONObject = {}
1989
1990        body["ctype"] = content_type
1991        body["searchtext"] = search_text
1992        body["tag"] = tag
1993
1994        if page is not undefined.UNDEFINED:
1995            body["currentpage"] = page
1996        else:
1997            body["currentpage"] = 1
1998
1999        if source is not undefined.UNDEFINED:
2000            body["source"] = source
2001        else:
2002            source = ""
2003        resp = await self._request(
2004            RequestMethod.GET, f"Content/Search/{locale}/", json=body
2005        )
2006        assert isinstance(resp, dict)
2007        return resp
2008
2009    async def search_content_by_tag_and_type(
2010        self,
2011        locale: str,
2012        tag: str,
2013        type: str,
2014        *,
2015        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2016    ) -> typedefs.JSONObject:
2017        body: typedefs.JSONObject = {}
2018        body["currentpage"] = 1 if page is undefined.UNDEFINED else page
2019        resp = await self._request(
2020            RequestMethod.GET,
2021            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
2022            json=body,
2023        )
2024        assert isinstance(resp, dict)
2025        return resp
2026
2027    async def search_help_articles(
2028        self, text: str, size: str, /
2029    ) -> typedefs.JSONObject:
2030        resp = await self._request(
2031            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
2032        )
2033        assert isinstance(resp, dict)
2034        return resp
2035
2036    async def fetch_topics_page(
2037        self,
2038        category_filter: int,
2039        group: int,
2040        date_filter: int,
2041        sort: typing.Union[str, bytes],
2042        *,
2043        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2044        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2045        tag_filter: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2046    ) -> typedefs.JSONObject:
2047
2048        body: typedefs.JSONObject = {}
2049        if locales is not undefined.UNDEFINED:
2050            body["locales"] = ",".join(str(locales))
2051        else:
2052            body["locales"] = ",".join([])
2053
2054        if tag_filter is not undefined.UNDEFINED:
2055            body["tagstring"] = tag_filter
2056        else:
2057            body["tagstring"] = ""
2058
2059        page = 0 if page is not undefined.UNDEFINED else page
2060
2061        resp = await self._request(
2062            RequestMethod.GET,
2063            f"Forum/GetTopicsPaged/{page}/{0}/{group}/{sort!s}/{date_filter}/{category_filter}/",
2064            json=body,
2065        )
2066        assert isinstance(resp, dict)
2067        return resp
2068
2069    async def fetch_core_topics_page(
2070        self,
2071        category_filter: int,
2072        date_filter: int,
2073        sort: typing.Union[str, bytes],
2074        *,
2075        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2076        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2077    ) -> typedefs.JSONObject:
2078        body: typedefs.JSONObject = {}
2079
2080        if locales is not undefined.UNDEFINED:
2081            body["locales"] = ",".join(str(locales))
2082        else:
2083            body["locales"] = ",".join([])
2084
2085        resp = await self._request(
2086            RequestMethod.GET,
2087            f"Forum/GetCoreTopicsPaged/{0 if page is undefined.UNDEFINED else page}"
2088            f"/{sort!s}/{date_filter}/{category_filter}/",
2089            json=body,
2090        )
2091        assert isinstance(resp, dict)
2092        return resp
2093
2094    async def fetch_posts_threaded_page(
2095        self,
2096        parent_post: bool,
2097        page: int,
2098        page_size: int,
2099        parent_post_id: int,
2100        reply_size: int,
2101        root_thread_mode: bool,
2102        sort_mode: int,
2103        show_banned: typing.Optional[str] = None,
2104    ) -> typedefs.JSONObject:
2105        resp = await self._request(
2106            RequestMethod.GET,
2107            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
2108            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
2109            json={"showbanned": show_banned},
2110        )
2111        assert isinstance(resp, dict)
2112        return resp
2113
2114    async def fetch_posts_threaded_page_from_child(
2115        self,
2116        child_id: bool,
2117        page: int,
2118        page_size: int,
2119        reply_size: int,
2120        root_thread_mode: bool,
2121        sort_mode: int,
2122        show_banned: typing.Optional[str] = None,
2123    ) -> typedefs.JSONObject:
2124        resp = await self._request(
2125            RequestMethod.GET,
2126            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
2127            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
2128            json={"showbanned": show_banned},
2129        )
2130        assert isinstance(resp, dict)
2131        return resp
2132
2133    async def fetch_post_and_parent(
2134        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2135    ) -> typedefs.JSONObject:
2136        resp = await self._request(
2137            RequestMethod.GET,
2138            f"Forum/GetPostAndParent/{child_id}/",
2139            json={"showbanned": show_banned},
2140        )
2141        assert isinstance(resp, dict)
2142        return resp
2143
2144    async def fetch_posts_and_parent_awaiting(
2145        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2146    ) -> typedefs.JSONObject:
2147        resp = await self._request(
2148            RequestMethod.GET,
2149            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2150            json={"showbanned": show_banned},
2151        )
2152        assert isinstance(resp, dict)
2153        return resp
2154
2155    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2156        resp = await self._request(
2157            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
2158        )
2159        assert isinstance(resp, int)
2160        return resp
2161
2162    async def fetch_forum_tag_suggestions(
2163        self, partial_tag: str, /
2164    ) -> typedefs.JSONObject:
2165        resp = await self._request(
2166            RequestMethod.GET,
2167            "Forum/GetForumTagSuggestions/",
2168            json={"partialtag": partial_tag},
2169        )
2170        assert isinstance(resp, dict)
2171        return resp
2172
2173    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2174        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
2175        assert isinstance(resp, dict)
2176        return resp
2177
2178    async def fetch_recuirement_thread_summaries(self) -> typedefs.JSONArray:
2179        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
2180        assert isinstance(resp, list)
2181        return resp
2182
2183    async def fetch_recommended_groups(
2184        self,
2185        accecss_token: str,
2186        /,
2187        *,
2188        date_range: int = 0,
2189        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
2190    ) -> typedefs.JSONArray:
2191        resp = await self._request(
2192            RequestMethod.POST,
2193            f"GroupV2/Recommended/{int(group_type)}/{date_range}/",
2194            auth=accecss_token,
2195        )
2196        assert isinstance(resp, list)
2197        return resp
2198
2199    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2200        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2201        assert isinstance(resp, dict)
2202        return resp
2203
2204    async def fetch_user_clan_invite_setting(
2205        self,
2206        access_token: str,
2207        /,
2208        membership_type: typedefs.IntAnd[enums.MembershipType],
2209    ) -> bool:
2210        resp = await self._request(
2211            RequestMethod.GET,
2212            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2213            auth=access_token,
2214        )
2215        assert isinstance(resp, bool)
2216        return resp
2217
2218    async def fetch_banned_group_members(
2219        self, access_token: str, group_id: int, /, *, page: int = 1
2220    ) -> typedefs.JSONObject:
2221        resp = await self._request(
2222            RequestMethod.GET,
2223            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2224            auth=access_token,
2225        )
2226        assert isinstance(resp, dict)
2227        return resp
2228
2229    async def fetch_pending_group_memberships(
2230        self, access_token: str, group_id: int, /, *, current_page: int = 1
2231    ) -> typedefs.JSONObject:
2232        resp = await self._request(
2233            RequestMethod.GET,
2234            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2235            auth=access_token,
2236        )
2237        assert isinstance(resp, dict)
2238        return resp
2239
2240    async def fetch_invited_group_memberships(
2241        self, access_token: str, group_id: int, /, *, current_page: int = 1
2242    ) -> typedefs.JSONObject:
2243        resp = await self._request(
2244            RequestMethod.GET,
2245            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2246            auth=access_token,
2247        )
2248        assert isinstance(resp, dict)
2249        return resp
2250
2251    async def invite_member_to_group(
2252        self,
2253        access_token: str,
2254        /,
2255        group_id: int,
2256        membership_id: int,
2257        membership_type: typedefs.IntAnd[enums.MembershipType],
2258        *,
2259        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2260    ) -> typedefs.JSONObject:
2261        resp = await self._request(
2262            RequestMethod.POST,
2263            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2264            auth=access_token,
2265            json={"message": str(message)},
2266        )
2267        assert isinstance(resp, dict)
2268        return resp
2269
2270    async def cancel_group_member_invite(
2271        self,
2272        access_token: str,
2273        /,
2274        group_id: int,
2275        membership_id: int,
2276        membership_type: typedefs.IntAnd[enums.MembershipType],
2277    ) -> typedefs.JSONObject:
2278        resp = await self._request(
2279            RequestMethod.POST,
2280            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2281            auth=access_token,
2282        )
2283        assert isinstance(resp, dict)
2284        return resp
2285
2286    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2287        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2288        assert isinstance(resp, dict)
2289        return resp
2290
2291    async def fetch_historical_stats(
2292        self,
2293        character_id: int,
2294        membership_id: int,
2295        membership_type: typedefs.IntAnd[enums.MembershipType],
2296        day_start: datetime.datetime,
2297        day_end: datetime.datetime,
2298        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2299        modes: collections.Sequence[typedefs.IntAnd[enums.GameMode]],
2300        *,
2301        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2302    ) -> typedefs.JSONObject:
2303
2304        end, start = time.parse_date_range(day_end, day_start)
2305        resp = await self._request(
2306            RequestMethod.GET,
2307            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2308            json={
2309                "dayend": end,
2310                "daystart": start,
2311                "groups": [str(int(group)) for group in groups],
2312                "modes": [str(int(mode)) for mode in modes],
2313                "periodType": int(period_type),
2314            },
2315        )
2316        assert isinstance(resp, dict)
2317        return resp
2318
2319    async def fetch_historical_stats_for_account(
2320        self,
2321        membership_id: int,
2322        membership_type: typedefs.IntAnd[enums.MembershipType],
2323        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2324    ) -> typedefs.JSONObject:
2325        resp = await self._request(
2326            RequestMethod.GET,
2327            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2328            json={"groups": [str(int(group)) for group in groups]},
2329        )
2330        assert isinstance(resp, dict)
2331        return resp
2332
2333    async def fetch_aggregated_activity_stats(
2334        self,
2335        character_id: int,
2336        membership_id: int,
2337        membership_type: typedefs.IntAnd[enums.MembershipType],
2338        /,
2339    ) -> typedefs.JSONObject:
2340        resp = await self._request(
2341            RequestMethod.GET,
2342            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2343            f"Character/{character_id}/Stats/AggregateActivityStats/",
2344        )
2345        assert isinstance(resp, dict)
2346        return resp

A RESTful client implementation for Bungie's API.

This client is designed to only make HTTP requests and return JSON objects to provide RESTful functionality.

This client is also used within aiobungie.Client which deserialize those returned JSON objects using the factory into Pythonic data classes objects which provide Python functionality.

Example
import aiobungie

async def main():
    async with aiobungie.RESTClient("TOKEN") as rest_client:
        req = await rest_client.fetch_clan_members(4389205)
        clan_members = req['results']
        for member in clan_members:
            for k, v in member['destinyUserInfo'].items():
                print(k, v)
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • max_ratelimit_retries (int): The max retries number to retry if the request hit a 429 status code. Defaults to 3.
  • client_secret (typing.Optional[str]): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (typing.Optional[int]): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
RESTClient( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, max_retries: int = 4, max_ratelimit_retries: int = 3, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
417    def __init__(
418        self,
419        token: str,
420        /,
421        client_secret: typing.Optional[str] = None,
422        client_id: typing.Optional[int] = None,
423        *,
424        max_retries: int = 4,
425        max_ratelimit_retries: int = 3,
426        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
427    ) -> None:
428        self._session: typing.Optional[_Session] = None
429        self._lock: typing.Optional[asyncio.Lock] = None
430        self._client_secret = client_secret
431        self._client_id = client_id
432        self._token: str = token
433        self._max_retries = max_retries
434        self._max_rate_limit_retries = max_ratelimit_retries
435        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
436
437        self._set_debug_level(enable_debugging)
client_id: Optional[int]

Return the client id of this REST client if provided, Otherwise None.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

This mapping is useful for storing any kind of data that the user may need.

Example
import aiobungie

client = aiobungie.RESTClient(…)

async with client:
    # Fetch auth tokens and store them
    client.metadata["tokens"] = await client.fetch_access_token("code")

# Some other time.
async with client:
    # Retrieve the tokens
    tokens: aiobungie.OAuth2Response = client.metadata["tokens"]

    # Use them to fetch your user.
    user = await client.fetch_current_user_memberships(tokens.access_token)
is_alive: bool

Returns True if the REST client is alive and False otherwise.

@typing.final
async def close(self) -> None:
451    @typing.final
452    async def close(self) -> None:
453        session = self._get_session()
454        await session.close()
455        self._session = None

Close this REST client session if it was acquired.

This method is automatically called when using async with contextmanager.

Raises
  • RuntimeError: If the client is already closed.
@typing.final
def open(self) -> None:
457    @typing.final
458    def open(self) -> None:
459        """Open a new client session. This is called internally with contextmanager usage."""
460        if self.is_alive:
461            raise RuntimeError("Cannot open a new session while it's already open.")
462
463        self._session = _Session.create()

Open a new client session. This is called internally with contextmanager usage.

@typing.final
def enable_debugging( self, level: Union[Literal['TRACE'], bool, int] = False, file: Union[pathlib.Path, str, NoneType] = None, /) -> None:
465    @typing.final
466    def enable_debugging(
467        self,
468        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
469        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
470        /,
471    ) -> None:
472        self._set_debug_level(level, file)

Enables debugging for the REST calls.

Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
Parameters
  • level (str | bool | int): The level of debugging to enable.
  • file (pathlib.Path | str | None): The file path to write the debug logs to. If provided.
@typing.final
async def static_request( self, method: Union[aiobungie.RequestMethod, str], path: str, *, auth: Optional[str] = None, json: Optional[dict[str, Any]] = None) -> Union[dict[str, Any], list[Any], bytes, int, bool, NoneType]:
474    @typing.final
475    async def static_request(
476        self,
477        method: typing.Union[RequestMethod, str],
478        path: str,
479        *,
480        auth: typing.Optional[str] = None,
481        json: typing.Optional[dict[str, typing.Any]] = None,
482    ) -> ResponseSig:
483        return await self._request(method, path, auth=auth, json=json)

Perform an HTTP request given a valid Bungie endpoint.

Parameters
  • method (aiobungie.RequestMethod | str): The request method, This may be GET, POST, PUT, etc.
  • path (str): The Bungie endpoint or path. A path must look something like this Destiny2/3/Profile/46111239123/...
  • auth (str | None): An optional bearer token for methods that requires OAuth2 Authorization header.
  • json (dict[str, typing.Any] | None): An optional JSON data to include in the request.
Returns
  • aiobungie.rest.ResponseSig: The response payload.
@typing.final
def build_oauth2_url( self, client_id: Optional[int] = None) -> Optional[aiobungie.builders.OAuthURL]:
485    @typing.final
486    def build_oauth2_url(
487        self, client_id: typing.Optional[int] = None
488    ) -> typing.Optional[builders.OAuthURL]:
489        client_id = client_id or self._client_id
490        if client_id is None:
491            return None
492
493        return builders.OAuthURL(client_id=client_id)

Builds an OAuth2 URL using the provided user REST/Base client secret/id.

You can't get the complete string URL by using .compile() method.

Parameters
  • client_id (int | None): An optional client id to provide, If left None it will roll back to the id passed to the RESTClient, If both is None this method will return None.
Returns
async def fetch_oauth2_tokens(self, code: str, /) -> aiobungie.builders.OAuth2Response:
720    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
721
722        if not isinstance(self._client_id, int):
723            raise TypeError(
724                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
725            )
726
727        if not isinstance(self._client_secret, str):
728            raise TypeError(
729                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
730            )
731
732        headers = {
733            "client_secret": self._client_secret,
734        }
735
736        data = (
737            f"grant_type=authorization_code&code={code}"
738            f"&client_id={self._client_id}&client_secret={self._client_secret}"
739        )
740
741        response = await self._request(
742            RequestMethod.POST, "", headers=headers, data=data, oauth2=True
743        )
744        assert isinstance(response, dict)
745        return builders.OAuth2Response.build_response(response)

Makes a POST request and fetch the OAuth2 access_token and refresh token.

Parameters
  • code (str): The Authorization code received from the authorization endpoint found in the URL parameters.
Returns
Raises
async def refresh_access_token(self, refresh_token: str, /) -> aiobungie.builders.OAuth2Response:
747    async def refresh_access_token(
748        self, refresh_token: str, /
749    ) -> builders.OAuth2Response:
750        if not isinstance(self._client_id, int):
751            raise TypeError(
752                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
753            )
754
755        if not isinstance(self._client_secret, str):
756            raise TypeError(
757                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
758            )
759
760        data = {
761            "grant_type": "refresh_token",
762            "refresh_token": refresh_token,
763            "client_id": self._client_id,
764            "client_secret": self._client_secret,
765            "Content-Type": "application/x-www-form-urlencoded",
766        }
767
768        response = await self._request(RequestMethod.POST, "", data=data, oauth2=True)
769        assert isinstance(response, dict)
770        return builders.OAuth2Response.build_response(response)

Refresh OAuth2 access token given its refresh token.

Parameters
  • refresh_token (str): The refresh token.
Returns
async def fetch_bungie_user(self, id: int) -> dict[str, typing.Any]:
772    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
773        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
774        resp = await self._request(
775            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
776        )
777        assert isinstance(resp, dict)
778        return resp

Fetch a Bungie user by their id.

Parameters
  • id (int): The user id.
Returns
Raises
async def fetch_user_themes(self) -> list[typing.Any]:
780    async def fetch_user_themes(self) -> typedefs.JSONArray:
781        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
782        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
783        assert isinstance(resp, list)
784        return resp

Fetch all available user themes.

Returns
async def fetch_membership_from_id( self, id: int, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>, /) -> dict[str, typing.Any]:
786    async def fetch_membership_from_id(
787        self,
788        id: int,
789        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
790        /,
791    ) -> typedefs.JSONObject:
792        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
793        resp = await self._request(
794            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
795        )
796        assert isinstance(resp, dict)
797        return resp

Fetch Bungie user's memberships from their id.

Parameters
Returns
Raises
async def fetch_player( self, name: str, code: int, type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, /) -> list[typing.Any]:
799    async def fetch_player(
800        self,
801        name: str,
802        code: int,
803        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
804        /,
805    ) -> typedefs.JSONArray:
806        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
807        resp = await self._request(
808            RequestMethod.POST,
809            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
810            json={"displayName": name, "displayNameCode": code},
811        )
812        assert isinstance(resp, list)
813        return resp

Fetch a Destiny 2 Player.

Parameters
Returns
Raises
async def search_users(self, name: str, /) -> dict[str, typing.Any]:
815    async def search_users(self, name: str, /) -> typedefs.JSONObject:
816        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
817        resp = await self._request(
818            RequestMethod.POST,
819            "User/Search/GlobalName/0",
820            json={"displayNamePrefix": name},
821        )
822        assert isinstance(resp, dict)
823        return resp

Search for users by their global name and return all users who share this name.

Parameters
  • name (str): The user name.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: Optional[str] = None) -> dict[str, typing.Any]:
825    async def fetch_clan_from_id(
826        self, id: int, /, access_token: typing.Optional[str] = None
827    ) -> typedefs.JSONObject:
828        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
829        resp = await self._request(
830            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
831        )
832        assert isinstance(resp, dict)
833        return resp

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Other Parameters
  • access_token (typing.Optional[str]): An optional access token to make the request with.

    If the token was bound to a member of the clan, This field aiobungie.crates.Clan.current_user_membership will be available and will return the membership of the user who made this request.

Returns
Raises
async def fetch_clan( self, name: str, /, access_token: Optional[str] = None, *, type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
835    async def fetch_clan(
836        self,
837        name: str,
838        /,
839        access_token: typing.Optional[str] = None,
840        *,
841        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
842    ) -> typedefs.JSONObject:
843        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
844        resp = await self._request(
845            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
846        )
847        assert isinstance(resp, dict)
848        return resp

Fetch a Clan by its name. This method will return the first clan found with given name name.

Parameters
  • name (str): The clan name.
Other Parameters
Returns
Raises
async def fetch_clan_admins(self, clan_id: int, /) -> dict[str, typing.Any]:
850    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
851        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
852        resp = await self._request(
853            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
854        )
855        assert isinstance(resp, dict)
856        return resp

Fetch the admins and founder members of the clan.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_clan_conversations(self, clan_id: int, /) -> list[typing.Any]:
858    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
859        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
860        resp = await self._request(
861            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
862        )
863        assert isinstance(resp, list)
864        return resp

Fetch a clan's conversations.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_application(self, appid: int, /) -> dict[str, typing.Any]:
866    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
867        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
868        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
869        assert isinstance(resp, dict)
870        return resp

Fetch a Bungie Application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_character( self, member_id: int, membership_type: Union[int, aiobungie.MembershipType], character_id: int, components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> dict[str, typing.Any]:
872    async def fetch_character(
873        self,
874        member_id: int,
875        membership_type: typedefs.IntAnd[enums.MembershipType],
876        character_id: int,
877        components: list[enums.ComponentType],
878        auth: typing.Optional[str] = None,
879    ) -> typedefs.JSONObject:
880        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
881        collector = _collect_components(components)
882        response = await self._request(
883            RequestMethod.GET,
884            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
885            f"Character/{character_id}/?components={collector}",
886            auth=auth,
887        )
888        assert isinstance(response, dict)
889        return response

Fetch a Destiny 2 player's characters.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_activities( self, member_id: int, character_id: int, mode: Union[int, aiobungie.GameMode], membership_type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, *, page: int = 0, limit: int = 1) -> dict[str, typing.Any]:
891    async def fetch_activities(
892        self,
893        member_id: int,
894        character_id: int,
895        mode: typedefs.IntAnd[enums.GameMode],
896        membership_type: typedefs.IntAnd[
897            enums.MembershipType
898        ] = enums.MembershipType.ALL,
899        *,
900        page: int = 0,
901        limit: int = 1,
902    ) -> typedefs.JSONObject:
903        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
904        resp = await self._request(
905            RequestMethod.GET,
906            f"Destiny2/{int(membership_type)}/Account/"
907            f"{member_id}/Character/{character_id}/Stats/Activities"
908            f"/?mode={int(mode)}&count={limit}&page={page}",
909        )
910        assert isinstance(resp, dict)
911        return resp

Fetch a Destiny 2 activity for the specified user id and character.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve.
  • mode (aiobungie.typedefs.IntAnd[aiobungie.GameMode]): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
Returns
Raises
async def fetch_vendor_sales(self) -> dict[str, typing.Any]:
913    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
914        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
915        resp = await self._request(
916            RequestMethod.GET,
917            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
918        )
919        assert isinstance(resp, dict)
920        return resp
async def fetch_profile( self, membership_id: int, type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> dict[str, typing.Any]:
922    async def fetch_profile(
923        self,
924        membership_id: int,
925        type: typedefs.IntAnd[enums.MembershipType],
926        components: list[enums.ComponentType],
927        auth: typing.Optional[str] = None,
928    ) -> typedefs.JSONObject:
929        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
930        collector = _collect_components(components)
931        response = await self._request(
932            RequestMethod.GET,
933            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
934            auth=auth,
935        )
936        assert isinstance(response, dict)
937        return response

Fetch a bungie profile.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_entity(self, type: str, hash: int) -> dict[str, typing.Any]:
939    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
940        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
941        response = await self._request(
942            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
943        )
944        assert isinstance(response, dict)
945        return response

Fetch a Destiny definition item given its type and hash.

Parameters
  • type (str): Entity's type definition.
  • hash (int): Entity's hash.
Returns
async def fetch_inventory_item(self, hash: int, /) -> dict[str, typing.Any]:
947    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
948        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
949        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
950        assert isinstance(resp, dict)
951        return resp

Fetch a Destiny inventory item entity given a its hash.

Parameters
  • hash (int): Entity's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> dict[str, typing.Any]:
953    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
954        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
955        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
956        assert isinstance(resp, dict)
957        return resp

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def fetch_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
959    async def fetch_groups_for_member(
960        self,
961        member_id: int,
962        member_type: typedefs.IntAnd[enums.MembershipType],
963        /,
964        *,
965        filter: int = 0,
966        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
967    ) -> typedefs.JSONObject:
968        resp = await self._request(
969            RequestMethod.GET,
970            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
971        )
972        assert isinstance(resp, dict)
973        return resp

Fetch the information about the groups for a member.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
975    async def fetch_potential_groups_for_member(
976        self,
977        member_id: int,
978        member_type: typedefs.IntAnd[enums.MembershipType],
979        /,
980        *,
981        filter: int = 0,
982        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
983    ) -> typedefs.JSONObject:
984        resp = await self._request(
985            RequestMethod.GET,
986            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
987        )
988        assert isinstance(resp, dict)
989        return resp

Get information about the groups that a given member has applied to or been invited to.

Parameters
Other Parameters
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: Optional[str] = None, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> dict[str, typing.Any]:
 991    async def fetch_clan_members(
 992        self,
 993        clan_id: int,
 994        /,
 995        *,
 996        name: typing.Optional[str] = None,
 997        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 998    ) -> typedefs.JSONObject:
 999        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1000        resp = await self._request(
1001            RequestMethod.GET,
1002            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
1003        )
1004        assert isinstance(resp, dict)
1005        return resp

Fetch all Bungie Clan members.

Parameters
  • clan_id (builsins.int): The clans id
Other Parameters
Returns
Raises
async def fetch_hardlinked_credentials( self, credential: int, type: Union[int, aiobungie.CredentialType] = <CredentialType.STEAMID: 12>, /) -> dict[str, typing.Any]:
1007    async def fetch_hardlinked_credentials(
1008        self,
1009        credential: int,
1010        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
1011        /,
1012    ) -> typedefs.JSONObject:
1013        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1014        resp = await self._request(
1015            RequestMethod.GET,
1016            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
1017        )
1018        assert isinstance(resp, dict)
1019        return resp

Gets any hard linked membership given a credential.

Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
Returns
async def fetch_user_credentials(self, access_token: str, membership_id: int, /) -> list[typing.Any]:
1021    async def fetch_user_credentials(
1022        self, access_token: str, membership_id: int, /
1023    ) -> typedefs.JSONArray:
1024        resp = await self._request(
1025            RequestMethod.GET,
1026            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
1027            auth=access_token,
1028        )
1029        assert isinstance(resp, list)
1030        return resp

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def insert_socket_plug( self, action_token: str, /, instance_id: int, plug: Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1032    async def insert_socket_plug(
1033        self,
1034        action_token: str,
1035        /,
1036        instance_id: int,
1037        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1038        character_id: int,
1039        membership_type: typedefs.IntAnd[enums.MembershipType],
1040    ) -> typedefs.JSONObject:
1041
1042        if isinstance(plug, builders.PlugSocketBuilder):
1043            plug = plug.collect()
1044
1045        body = {
1046            "actionToken": action_token,
1047            "itemInstanceId": instance_id,
1048            "plug": plug,
1049            "characterId": character_id,
1050            "membershipType": int(membership_type),
1051        }
1052        resp = await self._request(
1053            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
1054        )
1055        assert isinstance(resp, dict)
1056        return resp

Insert a plug into a socketed item.

OAuth2: AdvancedWriteActions scope is required

Parameters
  • action_token (str): Action token provided by the AwaGetActionToken API call.
  • instance_id (int): The item instance id that's plug inserted.
  • plug (typing.Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.typedefs.IntAnd[aiobungie.MembershipType] The membership type.

Returns
Raises
async def insert_socket_plug_free( self, access_token: str, /, instance_id: int, plug: Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1058    async def insert_socket_plug_free(
1059        self,
1060        access_token: str,
1061        /,
1062        instance_id: int,
1063        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1064        character_id: int,
1065        membership_type: typedefs.IntAnd[enums.MembershipType],
1066    ) -> typedefs.JSONObject:
1067
1068        if isinstance(plug, builders.PlugSocketBuilder):
1069            plug = plug.collect()
1070
1071        body = {
1072            "itemInstanceId": instance_id,
1073            "plug": plug,
1074            "characterId": character_id,
1075            "membershipType": int(membership_type),
1076        }
1077        resp = await self._request(
1078            RequestMethod.POST,
1079            "Destiny2/Actions/Items/InsertSocketPlugFree",
1080            json=body,
1081            auth=access_token,
1082        )
1083        assert isinstance(resp, dict)
1084        return resp

Insert a plug into a socketed item. This doesn't require an Action token.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • instance_id (int): The item instance id that's plug inserted.
  • plug (typing.Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.typedefs.IntAnd[aiobungie.MembershipType] The membership type.

Returns
Raises
async def set_item_lock_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> int:
1086    async def set_item_lock_state(
1087        self,
1088        access_token: str,
1089        state: bool,
1090        /,
1091        item_id: int,
1092        character_id: int,
1093        membership_type: typedefs.IntAnd[enums.MembershipType],
1094    ) -> int:
1095        body = {
1096            "state": state,
1097            "itemId": item_id,
1098            "characterId": character_id,
1099            "membership_type": int(membership_type),
1100        }
1101        response = await self._request(
1102            RequestMethod.POST,
1103            "Destiny2/Actions/Items/SetLockState",
1104            json=body,
1105            auth=access_token,
1106        )
1107        assert isinstance(response, int)
1108        return response

Set the Lock State for an instanced item.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def set_quest_track_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> int:
1110    async def set_quest_track_state(
1111        self,
1112        access_token: str,
1113        state: bool,
1114        /,
1115        item_id: int,
1116        character_id: int,
1117        membership_type: typedefs.IntAnd[enums.MembershipType],
1118    ) -> int:
1119        body = {
1120            "state": state,
1121            "itemId": item_id,
1122            "characterId": character_id,
1123            "membership_type": int(membership_type),
1124        }
1125        response = await self._request(
1126            RequestMethod.POST,
1127            "Destiny2/Actions/Items/SetTrackedState",
1128            json=body,
1129            auth=access_token,
1130        )
1131        assert isinstance(response, int)
1132        return response

Set the Tracking State for an instanced Quest or Bounty.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def fetch_manifest_path(self) -> dict[str, typing.Any]:
1134    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1135        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1136        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1137        assert isinstance(path, dict)
1138        return path

Fetch the manifest JSON paths.

Returns
  • typedefs.JSONObject: The manifest JSON paths.
async def read_manifest_bytes(self, language: str = 'en', /) -> bytes:
1140    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1141        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1142        _ensure_manifest_language(language)
1143
1144        content = await self.fetch_manifest_path()
1145        resp = await self._request(
1146            RequestMethod.GET,
1147            content["mobileWorldContentPaths"][language],
1148            unwrapping="read",
1149            base=True,
1150        )
1151        assert isinstance(resp, bytes)
1152        return resp

Read raw manifest SQLite database bytes response.

This method can be used to write the bytes to zipped file and then extract it to get the manifest content.

Parameters
  • language (str): The manifest database language bytes to get.
Returns
  • bytes: The bytes to read and write the manifest database.
async def download_manifest( self, language: str = 'en', name: str = 'manifest', path: Union[pathlib.Path, str] = '.', *, force: bool = False) -> None:
1154    async def download_manifest(
1155        self,
1156        language: str = "en",
1157        name: str = "manifest",
1158        path: typing.Union[pathlib.Path, str] = ".",
1159        *,
1160        force: bool = False,
1161    ) -> None:
1162        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1163        complete_path = _get_path(name, path, sql=True)
1164
1165        if complete_path.exists() and force:
1166            if force:
1167                _LOG.info(
1168                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1169                )
1170                complete_path.unlink(missing_ok=True)
1171
1172                return await self.download_manifest(language, name, path, force=force)
1173
1174            else:
1175                raise FileExistsError(
1176                    "Manifest file already exists, "
1177                    "To force download, set the `force` parameter to `True`."
1178                )
1179
1180        _LOG.info(f"Downloading manifest. Location: {complete_path!s}")
1181        data_bytes = await self.read_manifest_bytes(language)
1182        await asyncio.get_running_loop().run_in_executor(
1183            None, _write_sqlite_bytes, data_bytes, path, name
1184        )

A helper method to download the manifest.

Note

This method downloads the sqlite database and not JSON. Use RESTInterface.download_json_manifest for the JSON version.

Parameters
  • language (str): The manifest language to download, Default is english.
  • path (str | pathlib.Path): The path to save the manifest sqlite database. Example "D:/", Default is the current directory.
  • name (str): The manifest database file name. Default is manifest
  • force (bool): Whether to force the download. Default is False. However if set to true the old file will get removed and a new one will being to download.
Returns
  • None
Raises
  • FileNotFoundError: If the manifest file exists and force is False.
  • ValueError: If the provided language was not recognized.
async def download_json_manifest( self, file_name: str = 'manifest', path: Union[str, pathlib.Path] = '.', language: str = 'en') -> None:
1186    async def download_json_manifest(
1187        self,
1188        file_name: str = "manifest",
1189        path: typing.Union[str, pathlib.Path] = ".",
1190        language: str = "en",
1191    ) -> None:
1192        _ensure_manifest_language(language)
1193
1194        _LOG.info(f"Downloading manifest JSON to {_get_path(file_name, path)!r}...")
1195
1196        content = await self.fetch_manifest_path()
1197        json_bytes = await self._request(
1198            RequestMethod.GET,
1199            content["jsonWorldContentPaths"][language],
1200            unwrapping="read",
1201            base=True,
1202        )
1203
1204        await asyncio.get_running_loop().run_in_executor(
1205            None, _write_json_bytes, json_bytes, file_name, path
1206        )
1207        _LOG.info("Finished downloading manifest JSON.")

Download the Bungie manifest json file.

Parameters
  • file_name (str): The file name to save the manifest json file. Default is manifest.
  • path (str | pathlib.Path): The path to save the manifest json file. Default is the current directory. Example "D:/"
  • language (str): The manifest database language bytes to get. Default is English.
async def fetch_manifest_version(self) -> str:
1209    async def fetch_manifest_version(self) -> str:
1210        return typing.cast(str, (await self.fetch_manifest_path())["version"])

Fetch the manifest version.

Returns
  • str: The manifest version.
async def fetch_linked_profiles( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, all: bool = False) -> dict[str, typing.Any]:
1212    async def fetch_linked_profiles(
1213        self,
1214        member_id: int,
1215        member_type: typedefs.IntAnd[enums.MembershipType],
1216        /,
1217        *,
1218        all: bool = False,
1219    ) -> typedefs.JSONObject:
1220        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1221        resp = await self._request(
1222            RequestMethod.GET,
1223            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1224        )
1225        assert isinstance(resp, dict)
1226        return resp

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether thry're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
async def fetch_clan_banners(self) -> dict[str, typing.Any]:
1228    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1229        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1230        resp = await self._request(
1231            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1232        )
1233        assert isinstance(resp, dict)
1234        return resp

Fetch the values of the clan banners.

Returns
async def fetch_public_milestones(self) -> dict[str, typing.Any]:
1236    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1237        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1238        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1239        assert isinstance(resp, dict)
1240        return resp

Fetch the available milestones.

Returns
async def fetch_public_milestone_content(self, milestone_hash: int, /) -> dict[str, typing.Any]:
1242    async def fetch_public_milestone_content(
1243        self, milestone_hash: int, /
1244    ) -> typedefs.JSONObject:
1245        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1246        resp = await self._request(
1247            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1248        )
1249        assert isinstance(resp, dict)
1250        return resp

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
async def fetch_current_user_memberships(self, access_token: str, /) -> dict[str, typing.Any]:
1252    async def fetch_current_user_memberships(
1253        self, access_token: str, /
1254    ) -> typedefs.JSONObject:
1255        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1256        resp = await self._request(
1257            RequestMethod.GET,
1258            "User/GetMembershipsForCurrentUser/",
1259            auth=access_token,
1260        )
1261        assert isinstance(resp, dict)
1262        return resp

Fetch a bungie user's accounts with the signed in user. This GET method requires a Bearer access token for the authorization.

This requires OAuth2 scope enabled and the valid Bearer access_token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def equip_item( self, access_token: str, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1264    async def equip_item(
1265        self,
1266        access_token: str,
1267        /,
1268        item_id: int,
1269        character_id: int,
1270        membership_type: typedefs.IntAnd[enums.MembershipType],
1271    ) -> None:
1272        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1273        payload = {
1274            "itemId": item_id,
1275            "characterId": character_id,
1276            "membershipType": int(membership_type),
1277        }
1278
1279        await self._request(
1280            RequestMethod.POST,
1281            "Destiny2/Actions/Items/EquipItem/",
1282            json=payload,
1283            auth=access_token,
1284        )

Equip an item to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item id.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type assocaiated with this player.
async def equip_items( self, access_token: str, /, item_ids: list[int], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1286    async def equip_items(
1287        self,
1288        access_token: str,
1289        /,
1290        item_ids: list[int],
1291        character_id: int,
1292        membership_type: typedefs.IntAnd[enums.MembershipType],
1293    ) -> None:
1294        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1295        payload = {
1296            "itemIds": item_ids,
1297            "characterId": character_id,
1298            "membershipType": int(membership_type),
1299        }
1300        await self._request(
1301            RequestMethod.POST,
1302            "Destiny2/Actions/Items/EquipItems/",
1303            json=payload,
1304            auth=access_token,
1305        )

Equip multiple items to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_ids (list[int]): A list of item ids.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type assocaiated with this player.
async def ban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], *, length: int = 0, comment: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1307    async def ban_clan_member(
1308        self,
1309        access_token: str,
1310        /,
1311        group_id: int,
1312        membership_id: int,
1313        membership_type: typedefs.IntAnd[enums.MembershipType],
1314        *,
1315        length: int = 0,
1316        comment: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1317    ) -> None:
1318        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1319        payload = {"comment": str(comment), "length": length}
1320        await self._request(
1321            RequestMethod.POST,
1322            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1323            json=payload,
1324            auth=access_token,
1325        )

Bans a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to ban.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Other Parameters
async def unban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1327    async def unban_clan_member(
1328        self,
1329        access_token: str,
1330        /,
1331        group_id: int,
1332        membership_id: int,
1333        membership_type: typedefs.IntAnd[enums.MembershipType],
1334    ) -> None:
1335        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1336        await self._request(
1337            RequestMethod.POST,
1338            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1339            auth=access_token,
1340        )

Unbans a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to unban.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1342    async def kick_clan_member(
1343        self,
1344        access_token: str,
1345        /,
1346        group_id: int,
1347        membership_id: int,
1348        membership_type: typedefs.IntAnd[enums.MembershipType],
1349    ) -> typedefs.JSONObject:
1350        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1351        resp = await self._request(
1352            RequestMethod.POST,
1353            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1354            auth=access_token,
1355        )
1356        assert isinstance(resp, dict)
1357        return resp

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Returns
async def edit_clan( self, access_token: str, /, group_id: int, *, name: Optional[str] = None, about: Optional[str] = None, motto: Optional[str] = None, theme: Optional[str] = None, tags: Optional[collections.abc.Sequence[str]] = None, is_public: Optional[bool] = None, locale: Optional[str] = None, avatar_image_index: Optional[int] = None, membership_option: Union[NoneType, int, aiobungie.MembershipOption] = None, allow_chat: Optional[bool] = None, chat_security: Optional[Literal[0, 1]] = None, call_sign: Optional[str] = None, homepage: Optional[Literal[0, 1, 2]] = None, enable_invite_messaging_for_admins: Optional[bool] = None, default_publicity: Optional[Literal[0, 1, 2]] = None, is_public_topic_admin: Optional[bool] = None) -> None:
1359    async def edit_clan(
1360        self,
1361        access_token: str,
1362        /,
1363        group_id: int,
1364        *,
1365        name: typedefs.NoneOr[str] = None,
1366        about: typedefs.NoneOr[str] = None,
1367        motto: typedefs.NoneOr[str] = None,
1368        theme: typedefs.NoneOr[str] = None,
1369        tags: typedefs.NoneOr[collections.Sequence[str]] = None,
1370        is_public: typedefs.NoneOr[bool] = None,
1371        locale: typedefs.NoneOr[str] = None,
1372        avatar_image_index: typedefs.NoneOr[int] = None,
1373        membership_option: typedefs.NoneOr[
1374            typedefs.IntAnd[enums.MembershipOption]
1375        ] = None,
1376        allow_chat: typedefs.NoneOr[bool] = None,
1377        chat_security: typedefs.NoneOr[typing.Literal[0, 1]] = None,
1378        call_sign: typedefs.NoneOr[str] = None,
1379        homepage: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1380        enable_invite_messaging_for_admins: typedefs.NoneOr[bool] = None,
1381        default_publicity: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1382        is_public_topic_admin: typedefs.NoneOr[bool] = None,
1383    ) -> None:
1384        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1385        payload = {
1386            "name": name,
1387            "about": about,
1388            "motto": motto,
1389            "theme": theme,
1390            "tags": tags,
1391            "isPublic": is_public,
1392            "avatarImageIndex": avatar_image_index,
1393            "isPublicTopicAdminOnly": is_public_topic_admin,
1394            "allowChat": allow_chat,
1395            "chatSecurity": chat_security,
1396            "callsign": call_sign,
1397            "homepage": homepage,
1398            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1399            "defaultPublicity": default_publicity,
1400            "locale": locale,
1401        }
1402        if membership_option is not None:
1403            payload["membershipOption"] = int(membership_option)
1404
1405        await self._request(
1406            RequestMethod.POST,
1407            f"GroupV2/{group_id}/Edit",
1408            json=payload,
1409            auth=access_token,
1410        )

Edit a clan.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id to edit.
Other Parameters
async def edit_clan_options( self, access_token: str, /, group_id: int, *, invite_permissions_override: Optional[bool] = None, update_culture_permissionOverride: Optional[bool] = None, host_guided_game_permission_override: Optional[Literal[0, 1, 2]] = None, update_banner_permission_override: Optional[bool] = None, join_level: Union[NoneType, int, aiobungie.ClanMemberType] = None) -> None:
1412    async def edit_clan_options(
1413        self,
1414        access_token: str,
1415        /,
1416        group_id: int,
1417        *,
1418        invite_permissions_override: typedefs.NoneOr[bool] = None,
1419        update_culture_permissionOverride: typedefs.NoneOr[bool] = None,
1420        host_guided_game_permission_override: typedefs.NoneOr[
1421            typing.Literal[0, 1, 2]
1422        ] = None,
1423        update_banner_permission_override: typedefs.NoneOr[bool] = None,
1424        join_level: typedefs.NoneOr[typedefs.IntAnd[enums.ClanMemberType]] = None,
1425    ) -> None:
1426
1427        payload = {
1428            "InvitePermissionOverride": invite_permissions_override,
1429            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1430            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1431            "UpdateBannerPermissionOverride": update_banner_permission_override,
1432            "JoinLevel": int(join_level) if join_level else None,
1433        }
1434
1435        await self._request(
1436            RequestMethod.POST,
1437            f"GroupV2/{group_id}/EditFounderOptions",
1438            json=payload,
1439            auth=access_token,
1440        )

Edit the clan options.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
Other Parameters
  • invite_permissions_override (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to invite new members to group Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • update_culture_permissionOverride (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to update group culture Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • host_guided_game_permission_override (aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): Minimum Member Level allowed to host guided games Always Allowed: Founder, Acting Founder, Admin Allowed Overrides: 0 -> None, 1 -> Beginner 2 -> Member. Default is Member for clans, None for groups, although this means nothing for groups.
  • update_banner_permission_override (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to update banner Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • join_level (aiobungie.ClanMemberType): Level to join a member at when accepting an invite, application, or joining an open clan. Default is aiobungie.ClanMemberType.BEGINNER
async def fetch_friends(self, access_token: str, /) -> dict[str, typing.Any]:
1442    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1443        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1444        resp = await self._request(
1445            RequestMethod.GET,
1446            "Social/Friends/",
1447            auth=access_token,
1448        )
1449        assert isinstance(resp, dict)
1450        return resp

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> dict[str, typing.Any]:
1452    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1453        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1454        resp = await self._request(
1455            RequestMethod.GET,
1456            "Social/Friends/Requests",
1457            auth=access_token,
1458        )
1459        assert isinstance(resp, dict)
1460        return resp

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1462    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1463        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1464        await self._request(
1465            RequestMethod.POST,
1466            f"Social/Friends/Requests/Accept/{member_id}",
1467            auth=access_token,
1468        )

Accepts a friend relationship with the target user. The user must be on your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to accept.
async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1470    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1471        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1472        await self._request(
1473            RequestMethod.POST,
1474            f"Social/Friends/Add/{member_id}",
1475            auth=access_token,
1476        )

Requests a friend relationship with the target user.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to send the request to.
async def decline_friend_request(self, access_token: str, /, member_id: int) -> None:
1478    async def decline_friend_request(
1479        self, access_token: str, /, member_id: int
1480    ) -> None:
1481        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1482        await self._request(
1483            RequestMethod.POST,
1484            f"Social/Friends/Requests/Decline/{member_id}",
1485            auth=access_token,
1486        )

Decline a friend request with the target user. The user must be in your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to decline.
async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1488    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1489        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1490        await self._request(
1491            RequestMethod.POST,
1492            f"Social/Friends/Remove/{member_id}",
1493            auth=access_token,
1494        )

Removes a friend from your friend list. The user must be in your friend list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove.
async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1496    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1497        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1498        await self._request(
1499            RequestMethod.POST,
1500            f"Social/Friends/Requests/Remove/{member_id}",
1501            auth=access_token,
1502        )

Removes a friend from your friend list requests. The user must be in your outgoing request list.

.. note : This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove from the requested friend list.
async def approve_all_pending_group_users( self, access_token: str, /, group_id: int, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1504    async def approve_all_pending_group_users(
1505        self,
1506        access_token: str,
1507        /,
1508        group_id: int,
1509        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1510    ) -> None:
1511        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1512        await self._request(
1513            RequestMethod.POST,
1514            f"GroupV2/{group_id}/Members/ApproveAll",
1515            auth=access_token,
1516            json={"message": str(message)},
1517        )

Apporve all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
async def deny_all_pending_group_users( self, access_token: str, /, group_id: int, *, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1519    async def deny_all_pending_group_users(
1520        self,
1521        access_token: str,
1522        /,
1523        group_id: int,
1524        *,
1525        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1526    ) -> None:
1527        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1528        await self._request(
1529            RequestMethod.POST,
1530            f"GroupV2/{group_id}/Members/DenyAll",
1531            auth=access_token,
1532            json={"message": str(message)},
1533        )

Deny all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
async def add_optional_conversation( self, access_token: str, /, group_id: int, *, name: Union[aiobungie.UndefinedType, str] = UNDEFINED, security: Literal[0, 1] = 0) -> None:
1535    async def add_optional_conversation(
1536        self,
1537        access_token: str,
1538        /,
1539        group_id: int,
1540        *,
1541        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1542        security: typing.Literal[0, 1] = 0,
1543    ) -> None:
1544        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1545        payload = {"chatName": str(name), "chatSecurity": security}
1546        await self._request(
1547            RequestMethod.POST,
1548            f"GroupV2/{group_id}/OptionalConversations/Add",
1549            json=payload,
1550            auth=access_token,
1551        )

Add a new chat channel to a group.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other parameters

name: aiobungie.UndefinedOr[str] The chat name. Default to UNDEFINED security: typing.Literal[0, 1] The security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`
async def edit_optional_conversation( self, access_token: str, /, group_id: int, conversation_id: int, *, name: Union[aiobungie.UndefinedType, str] = UNDEFINED, security: Literal[0, 1] = 0, enable_chat: bool = False) -> None:
1553    async def edit_optional_conversation(
1554        self,
1555        access_token: str,
1556        /,
1557        group_id: int,
1558        conversation_id: int,
1559        *,
1560        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1561        security: typing.Literal[0, 1] = 0,
1562        enable_chat: bool = False,
1563    ) -> None:
1564        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1565        payload = {
1566            "chatEnabled": enable_chat,
1567            "chatName": str(name),
1568            "chatSecurity": security,
1569        }
1570        await self._request(
1571            RequestMethod.POST,
1572            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1573            json=payload,
1574            auth=access_token,
1575        )

Edit the settings of this chat channel.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
  • conversation_id (int): The conversation/chat id.
Other parameters

name: aiobungie.UndefinedOr[str] The new chat name. Default to UNDEFINED security: typing.Literal[0, 1] The new security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`

enable_chat : bool Whether to enable chatting or not. If set to True then chatting will be enabled. Otherwise it will be disabled.

async def transfer_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: Union[int, aiobungie.MembershipType], *, stack_size: int = 1, vault: bool = False) -> None:
1577    async def transfer_item(
1578        self,
1579        access_token: str,
1580        /,
1581        item_id: int,
1582        item_hash: int,
1583        character_id: int,
1584        member_type: typedefs.IntAnd[enums.MembershipType],
1585        *,
1586        stack_size: int = 1,
1587        vault: bool = False,
1588    ) -> None:
1589        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1590        payload = {
1591            "characterId": character_id,
1592            "membershipType": int(member_type),
1593            "itemId": item_id,
1594            "itemReferenceHash": item_hash,
1595            "stackSize": stack_size,
1596            "transferToVault": vault,
1597        }
1598        await self._request(
1599            RequestMethod.POST,
1600            "Destiny2/Actions/Items/TransferItem",
1601            json=payload,
1602            auth=access_token,
1603        )

Transfer an item from / to your vault.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id you to transfer.
  • item_hash (int): The item hash.
  • character_id (int): The character id to transfer the item from/to.
  • member_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • valut (bool): Whether to trasnfer this item to your valut or not. Defaults to False.
async def pull_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: Union[int, aiobungie.MembershipType], *, stack_size: int = 1, vault: bool = False) -> None:
1605    async def pull_item(
1606        self,
1607        access_token: str,
1608        /,
1609        item_id: int,
1610        item_hash: int,
1611        character_id: int,
1612        member_type: typedefs.IntAnd[enums.MembershipType],
1613        *,
1614        stack_size: int = 1,
1615        vault: bool = False,
1616    ) -> None:
1617        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1618        payload = {
1619            "characterId": character_id,
1620            "membershipType": int(member_type),
1621            "itemId": item_id,
1622            "itemReferenceHash": item_hash,
1623            "stackSize": stack_size,
1624            "transferToVault": vault,
1625        }
1626        await self._request(
1627            RequestMethod.POST,
1628            "Destiny2/Actions/Items/PullFromPostmaster",
1629            json=payload,
1630            auth=access_token,
1631        )

pull an item from the postmaster.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id to pull.
  • item_hash (int): The item hash.
  • character_id (int): The character id to pull the item to.
  • member_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • valut (bool): Whether to pill this item to your valut or not. Defaults to False.
async def fetch_fireteams( self, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform] = <FireteamPlatform.ANY: 0>, language: Union[aiobungie.FireteamLanguage, str] = <FireteamLanguage.ALL: >, date_range: Union[int, aiobungie.FireteamDate] = <FireteamDate.ALL: 0>, page: int = 0, slots_filter: int = 0) -> dict[str, typing.Any]:
1633    async def fetch_fireteams(
1634        self,
1635        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1636        *,
1637        platform: typedefs.IntAnd[
1638            fireteams.FireteamPlatform
1639        ] = fireteams.FireteamPlatform.ANY,
1640        language: typing.Union[
1641            fireteams.FireteamLanguage, str
1642        ] = fireteams.FireteamLanguage.ALL,
1643        date_range: typedefs.IntAnd[
1644            fireteams.FireteamDate
1645        ] = fireteams.FireteamDate.ALL,
1646        page: int = 0,
1647        slots_filter: int = 0,
1648    ) -> typedefs.JSONObject:
1649        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1650        resp = await self._request(
1651            RequestMethod.GET,
1652            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1653        )
1654        assert isinstance(resp, dict)
1655        return resp

Fetch public Bungie fireteams with open slots.

Parameters
Other Parameters
Returns
async def fetch_avaliable_clan_fireteams( self, access_token: str, group_id: int, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], date_range: Union[int, aiobungie.FireteamDate] = <FireteamDate.ALL: 0>, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> dict[str, typing.Any]:
1657    async def fetch_avaliable_clan_fireteams(
1658        self,
1659        access_token: str,
1660        group_id: int,
1661        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1662        *,
1663        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1664        language: typing.Union[fireteams.FireteamLanguage, str],
1665        date_range: typedefs.IntAnd[
1666            fireteams.FireteamDate
1667        ] = fireteams.FireteamDate.ALL,
1668        page: int = 0,
1669        public_only: bool = False,
1670        slots_filter: int = 0,
1671    ) -> typedefs.JSONObject:
1672        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1673        resp = await self._request(
1674            RequestMethod.GET,
1675            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1676            json={"langFilter": str(language)},
1677            auth=access_token,
1678        )
1679        assert isinstance(resp, dict)
1680        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]): The fireteam activity type.
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (aiobungie.typedefs.IntAnd[aiobungie.FireteamDate]): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> dict[str, typing.Any]:
1682    async def fetch_clan_fireteam(
1683        self, access_token: str, fireteam_id: int, group_id: int
1684    ) -> typedefs.JSONObject:
1685        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1686        resp = await self._request(
1687            RequestMethod.GET,
1688            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1689            auth=access_token,
1690        )
1691        assert isinstance(resp, dict)
1692        return resp

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], filtered: bool = True, page: int = 0) -> dict[str, typing.Any]:
1694    async def fetch_my_clan_fireteams(
1695        self,
1696        access_token: str,
1697        group_id: int,
1698        *,
1699        include_closed: bool = True,
1700        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1701        language: typing.Union[fireteams.FireteamLanguage, str],
1702        filtered: bool = True,
1703        page: int = 0,
1704    ) -> typedefs.JSONObject:
1705        payload = {"groupFilter": filtered, "langFilter": str(language)}
1706        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1707        resp = await self._request(
1708            RequestMethod.GET,
1709            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1710            json=payload,
1711            auth=access_token,
1712        )
1713        assert isinstance(resp, dict)
1714        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_private_clan_fireteams(self, access_token: str, group_id: int, /) -> int:
1716    async def fetch_private_clan_fireteams(
1717        self, access_token: str, group_id: int, /
1718    ) -> int:
1719        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1720        resp = await self._request(
1721            RequestMethod.GET,
1722            f"Fireteam/Clan/{group_id}/ActiveCount",
1723            auth=access_token,
1724        )
1725        assert isinstance(resp, int)
1726        return resp

Fetch the active count of the clan fireteams that are only private.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id.
Returns
  • int: The active fireteams count. Max value returned is 25.
async def fetch_post_activity(self, instance_id: int, /) -> dict[str, typing.Any]:
1728    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1729        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1730        resp = await self._request(
1731            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1732        )
1733        assert isinstance(resp, dict)
1734        return resp

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> dict[str, typing.Any]:
1736    async def search_entities(
1737        self, name: str, entity_type: str, *, page: int = 0
1738    ) -> typedefs.JSONObject:
1739        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1740        resp = await self._request(
1741            RequestMethod.GET,
1742            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1743            json={"page": page},
1744        )
1745        assert isinstance(resp, dict)
1746        return resp

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1748    async def fetch_unique_weapon_history(
1749        self,
1750        membership_id: int,
1751        character_id: int,
1752        membership_type: typedefs.IntAnd[enums.MembershipType],
1753    ) -> typedefs.JSONObject:
1754        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1755        resp = await self._request(
1756            RequestMethod.GET,
1757            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1758        )
1759        assert isinstance(resp, dict)
1760        return resp

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
Returns
async def fetch_item( self, member_id: int, item_id: int, membership_type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType]) -> dict[str, typing.Any]:
1762    async def fetch_item(
1763        self,
1764        member_id: int,
1765        item_id: int,
1766        membership_type: typedefs.IntAnd[enums.MembershipType],
1767        components: list[enums.ComponentType],
1768    ) -> typedefs.JSONObject:
1769        collector = _collect_components(components)
1770        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1771        resp = await self._request(
1772            RequestMethod.GET,
1773            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1774        )
1775        assert isinstance(resp, dict)
1776        return resp

Fetch an instanced Destiny 2 item's details.

Parameters
Returns
async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> dict[str, typing.Any]:
1778    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1779        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1780        resp = await self._request(
1781            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1782        )
1783        assert isinstance(resp, dict)
1784        return resp

Fetch the weekly reward state for a clan.

Parameters
  • clan_id (int): The clan id.
Returns
async def fetch_available_locales(self) -> dict[str, typing.Any]:
1786    async def fetch_available_locales(self) -> typedefs.JSONObject:
1787        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1788        resp = await self._request(
1789            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1790        )
1791        assert isinstance(resp, dict)
1792        return resp

Fetch available locales at Bungie.

Returns
async def fetch_common_settings(self) -> dict[str, typing.Any]:
1794    async def fetch_common_settings(self) -> typedefs.JSONObject:
1795        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1796        resp = await self._request(RequestMethod.GET, "Settings")
1797        assert isinstance(resp, dict)
1798        return resp

Fetch the common settings used by Bungie's envirotment.

Returns
async def fetch_user_systems_overrides(self) -> dict[str, typing.Any]:
1800    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1801        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1802        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1803        assert isinstance(resp, dict)
1804        return resp

Fetch a user's specific system overrides.

Returns
async def fetch_global_alerts(self, *, include_streaming: bool = False) -> list[typing.Any]:
1806    async def fetch_global_alerts(
1807        self, *, include_streaming: bool = False
1808    ) -> typedefs.JSONArray:
1809        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1810        resp = await self._request(
1811            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1812        )
1813        assert isinstance(resp, list)
1814        return resp

Fetch any active global alerts.

Parameters
  • include_streaming (bool): If True, the returned results will include streaming alerts. Default is False.
Returns
async def awainitialize_request( self, access_token: str, type: Literal[0, 1], membership_type: Union[int, aiobungie.MembershipType], /, *, affected_item_id: Optional[int] = None, character_id: Optional[int] = None) -> dict[str, typing.Any]:
1816    async def awainitialize_request(
1817        self,
1818        access_token: str,
1819        type: typing.Literal[0, 1],
1820        membership_type: typedefs.IntAnd[enums.MembershipType],
1821        /,
1822        *,
1823        affected_item_id: typing.Optional[int] = None,
1824        character_id: typing.Optional[int] = None,
1825    ) -> typedefs.JSONObject:
1826        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1827
1828        body = {"type": type, "membershipType": int(membership_type)}
1829
1830        if affected_item_id is not None:
1831            body["affectedItemId"] = affected_item_id
1832
1833        if character_id is not None:
1834            body["characterId"] = character_id
1835
1836        resp = await self._request(
1837            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1838        )
1839        assert isinstance(resp, dict)
1840        return resp

Initialize a request to perform an advanced write action.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • type (typing.Literal[0, 1]): Type of the advanced write action. Its either 0 or 1. If set to 0 that means it None. Otherwise if 1 that means its insert plugs.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type of the account to modify.
Other Parameters
  • affected_item_id (typing.Optional[int]): Item instance ID the action shall be applied to. This is optional for all but a new AwaType values.
  • character_id (typing.Optional[int]): The Destiny character ID to perform this action on.
Returns
async def awaget_action_token(self, access_token: str, correlation_id: str, /) -> dict[str, typing.Any]:
1842    async def awaget_action_token(
1843        self, access_token: str, correlation_id: str, /
1844    ) -> typedefs.JSONObject:
1845        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1846        resp = await self._request(
1847            RequestMethod.POST,
1848            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1849            auth=access_token,
1850        )
1851        assert isinstance(resp, dict)
1852        return resp

Returns the action token if user approves the request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • correlation_id (str): The identifier for the advanced write action request.
Returns
async def awa_provide_authorization_result( self, access_token: str, selection: int, correlation_id: str, nonce: collections.abc.MutableSequence[typing.Union[str, bytes]]) -> int:
1854    async def awa_provide_authorization_result(
1855        self,
1856        access_token: str,
1857        selection: int,
1858        correlation_id: str,
1859        nonce: collections.MutableSequence[typing.Union[str, bytes]],
1860    ) -> int:
1861        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1862
1863        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1864
1865        resp = await self._request(
1866            RequestMethod.POST,
1867            "Destiny2/Awa/AwaProvideAuthorizationResult",
1868            json=body,
1869            auth=access_token,
1870        )
1871        assert isinstance(resp, int)
1872        return resp

Provide the result of the user interaction. Called by the Bungie Destiny App to approve or reject a request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • selection (int): Indication of the selection the user has made (Approving or rejecting the action)
  • correlation_id (str): Correlation ID of the request.
  • nonce (collections.MutableSequence[str, bytes]): Secret nonce received via the PUSH notification.
Returns
  • int: ...
async def fetch_vendors( self, access_token: str, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], /, components: list[aiobungie.ComponentType], filter: Optional[int] = None) -> dict[str, typing.Any]:
1874    async def fetch_vendors(
1875        self,
1876        access_token: str,
1877        character_id: int,
1878        membership_id: int,
1879        membership_type: typedefs.IntAnd[enums.MembershipType],
1880        /,
1881        components: list[enums.ComponentType],
1882        filter: typing.Optional[int] = None,
1883    ) -> typedefs.JSONObject:
1884        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1885        components_ = _collect_components(components)
1886        route = (
1887            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1888            f"/Character/{character_id}/Vendors/?components={components_}"
1889        )
1890
1891        if filter is not None:
1892            route = route + f"&filter={filter}"
1893
1894        resp = await self._request(
1895            RequestMethod.GET,
1896            route,
1897            auth=access_token,
1898        )
1899        assert isinstance(resp, dict)
1900        return resp

Get currently available vendors from the list of vendors that can possibly have rotating inventory.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type to return the vendor info for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Other Parameters
  • filter (int): Filters the type of items returned from the vendor. This can be left to None.
Returns
async def fetch_vendor( self, access_token: str, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], vendor_hash: int, /, components: list[aiobungie.ComponentType]) -> dict[str, typing.Any]:
1902    async def fetch_vendor(
1903        self,
1904        access_token: str,
1905        character_id: int,
1906        membership_id: int,
1907        membership_type: typedefs.IntAnd[enums.MembershipType],
1908        vendor_hash: int,
1909        /,
1910        components: list[enums.ComponentType],
1911    ) -> typedefs.JSONObject:
1912        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1913        components_ = _collect_components(components)
1914        resp = await self._request(
1915            RequestMethod.GET,
1916            (
1917                f"Platform/Destiny2/{int(membership_type)}/Profile/{membership_id}"
1918                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1919            ),
1920            auth=access_token,
1921        )
1922        assert isinstance(resp, dict)
1923        return resp

Fetch details for a specific vendor.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type to return the vendor info for.
  • vendor_hash (int): The vendor hash to return the details for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Returns
async def fetch_application_api_usage( self, access_token: str, application_id: int, /, *, start: Optional[datetime.datetime] = None, end: Optional[datetime.datetime] = None) -> dict[str, typing.Any]:
1925    async def fetch_application_api_usage(
1926        self,
1927        access_token: str,
1928        application_id: int,
1929        /,
1930        *,
1931        start: typing.Optional[datetime.datetime] = None,
1932        end: typing.Optional[datetime.datetime] = None,
1933    ) -> typedefs.JSONObject:
1934
1935        end_date, start_date = time.parse_date_range(end, start)
1936        resp = await self._request(
1937            RequestMethod.GET,
1938            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1939            auth=access_token,
1940        )
1941        assert isinstance(resp, dict)
1942        return resp

Fetch a Bungie application's API usage.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • application_id (int): The application id to get.
Other Parameters
  • start (typing.Optional[datetime.datetime]): A datetime object can be used to collect the start of the application usage. This is limited and can go back to 30 days maximum.

    If this is left to None. It will return the last 24 hours.

  • end (typing.Optional[datetime.datetime]): A datetime object can be used to collect the end of the application usage.

    If this is left to None. It will return now.

Example
import datetime

# Fetch data from 2021 Dec 10th to 2021 Dec 20th
await fetch_application_api_usage(
    start=datetime.datetime(2021, 12, 10), end=datetime.datetime(2021, 12, 20)
)
Returns
async def fetch_bungie_applications(self) -> list[typing.Any]:
1944    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1945        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1946        assert isinstance(resp, list)
1947        return resp

Fetch details for applications created by Bungie.

Returns
async def fetch_content_type(self, type: str, /) -> dict[str, typing.Any]:
1949    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1950        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1951        assert isinstance(resp, dict)
1952        return resp
async def fetch_content_by_id( self, id: int, locale: str, /, *, head: bool = False) -> dict[str, typing.Any]:
1954    async def fetch_content_by_id(
1955        self, id: int, locale: str, /, *, head: bool = False
1956    ) -> typedefs.JSONObject:
1957        resp = await self._request(
1958            RequestMethod.GET,
1959            f"Content/GetContentById/{id}/{locale}/",
1960            json={"head": head},
1961        )
1962        assert isinstance(resp, dict)
1963        return resp
async def fetch_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, head: bool = False) -> dict[str, typing.Any]:
1965    async def fetch_content_by_tag_and_type(
1966        self, locale: str, tag: str, type: str, *, head: bool = False
1967    ) -> typedefs.JSONObject:
1968        resp = await self._request(
1969            RequestMethod.GET,
1970            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1971            json={"head": head},
1972        )
1973        assert isinstance(resp, dict)
1974        return resp
async def search_content_with_text( self, locale: str, /, content_type: str, search_text: str, tag: str, *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, source: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
1976    async def search_content_with_text(
1977        self,
1978        locale: str,
1979        /,
1980        content_type: str,
1981        search_text: str,
1982        tag: str,
1983        *,
1984        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1985        source: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1986    ) -> typedefs.JSONObject:
1987
1988        body: typedefs.JSONObject = {}
1989
1990        body["ctype"] = content_type
1991        body["searchtext"] = search_text
1992        body["tag"] = tag
1993
1994        if page is not undefined.UNDEFINED:
1995            body["currentpage"] = page
1996        else:
1997            body["currentpage"] = 1
1998
1999        if source is not undefined.UNDEFINED:
2000            body["source"] = source
2001        else:
2002            source = ""
2003        resp = await self._request(
2004            RequestMethod.GET, f"Content/Search/{locale}/", json=body
2005        )
2006        assert isinstance(resp, dict)
2007        return resp
async def search_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED) -> dict[str, typing.Any]:
2009    async def search_content_by_tag_and_type(
2010        self,
2011        locale: str,
2012        tag: str,
2013        type: str,
2014        *,
2015        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2016    ) -> typedefs.JSONObject:
2017        body: typedefs.JSONObject = {}
2018        body["currentpage"] = 1 if page is undefined.UNDEFINED else page
2019        resp = await self._request(
2020            RequestMethod.GET,
2021            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
2022            json=body,
2023        )
2024        assert isinstance(resp, dict)
2025        return resp
async def search_help_articles(self, text: str, size: str, /) -> dict[str, typing.Any]:
2027    async def search_help_articles(
2028        self, text: str, size: str, /
2029    ) -> typedefs.JSONObject:
2030        resp = await self._request(
2031            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
2032        )
2033        assert isinstance(resp, dict)
2034        return resp
async def fetch_topics_page( self, category_filter: int, group: int, date_filter: int, sort: Union[str, bytes], *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, locales: Union[aiobungie.UndefinedType, collections.abc.Iterable[str]] = UNDEFINED, tag_filter: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
2036    async def fetch_topics_page(
2037        self,
2038        category_filter: int,
2039        group: int,
2040        date_filter: int,
2041        sort: typing.Union[str, bytes],
2042        *,
2043        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2044        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2045        tag_filter: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2046    ) -> typedefs.JSONObject:
2047
2048        body: typedefs.JSONObject = {}
2049        if locales is not undefined.UNDEFINED:
2050            body["locales"] = ",".join(str(locales))
2051        else:
2052            body["locales"] = ",".join([])
2053
2054        if tag_filter is not undefined.UNDEFINED:
2055            body["tagstring"] = tag_filter
2056        else:
2057            body["tagstring"] = ""
2058
2059        page = 0 if page is not undefined.UNDEFINED else page
2060
2061        resp = await self._request(
2062            RequestMethod.GET,
2063            f"Forum/GetTopicsPaged/{page}/{0}/{group}/{sort!s}/{date_filter}/{category_filter}/",
2064            json=body,
2065        )
2066        assert isinstance(resp, dict)
2067        return resp
async def fetch_core_topics_page( self, category_filter: int, date_filter: int, sort: Union[str, bytes], *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, locales: Union[aiobungie.UndefinedType, collections.abc.Iterable[str]] = UNDEFINED) -> dict[str, typing.Any]:
2069    async def fetch_core_topics_page(
2070        self,
2071        category_filter: int,
2072        date_filter: int,
2073        sort: typing.Union[str, bytes],
2074        *,
2075        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2076        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2077    ) -> typedefs.JSONObject:
2078        body: typedefs.JSONObject = {}
2079
2080        if locales is not undefined.UNDEFINED:
2081            body["locales"] = ",".join(str(locales))
2082        else:
2083            body["locales"] = ",".join([])
2084
2085        resp = await self._request(
2086            RequestMethod.GET,
2087            f"Forum/GetCoreTopicsPaged/{0 if page is undefined.UNDEFINED else page}"
2088            f"/{sort!s}/{date_filter}/{category_filter}/",
2089            json=body,
2090        )
2091        assert isinstance(resp, dict)
2092        return resp
async def fetch_posts_threaded_page( self, parent_post: bool, page: int, page_size: int, parent_post_id: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2094    async def fetch_posts_threaded_page(
2095        self,
2096        parent_post: bool,
2097        page: int,
2098        page_size: int,
2099        parent_post_id: int,
2100        reply_size: int,
2101        root_thread_mode: bool,
2102        sort_mode: int,
2103        show_banned: typing.Optional[str] = None,
2104    ) -> typedefs.JSONObject:
2105        resp = await self._request(
2106            RequestMethod.GET,
2107            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
2108            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
2109            json={"showbanned": show_banned},
2110        )
2111        assert isinstance(resp, dict)
2112        return resp
async def fetch_posts_threaded_page_from_child( self, child_id: bool, page: int, page_size: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2114    async def fetch_posts_threaded_page_from_child(
2115        self,
2116        child_id: bool,
2117        page: int,
2118        page_size: int,
2119        reply_size: int,
2120        root_thread_mode: bool,
2121        sort_mode: int,
2122        show_banned: typing.Optional[str] = None,
2123    ) -> typedefs.JSONObject:
2124        resp = await self._request(
2125            RequestMethod.GET,
2126            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
2127            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
2128            json={"showbanned": show_banned},
2129        )
2130        assert isinstance(resp, dict)
2131        return resp
async def fetch_post_and_parent( self, child_id: int, /, *, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2133    async def fetch_post_and_parent(
2134        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2135    ) -> typedefs.JSONObject:
2136        resp = await self._request(
2137            RequestMethod.GET,
2138            f"Forum/GetPostAndParent/{child_id}/",
2139            json={"showbanned": show_banned},
2140        )
2141        assert isinstance(resp, dict)
2142        return resp
async def fetch_posts_and_parent_awaiting( self, child_id: int, /, *, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2144    async def fetch_posts_and_parent_awaiting(
2145        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2146    ) -> typedefs.JSONObject:
2147        resp = await self._request(
2148            RequestMethod.GET,
2149            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2150            json={"showbanned": show_banned},
2151        )
2152        assert isinstance(resp, dict)
2153        return resp
async def fetch_topic_for_content(self, content_id: int, /) -> int:
2155    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2156        resp = await self._request(
2157            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
2158        )
2159        assert isinstance(resp, int)
2160        return resp
async def fetch_forum_tag_suggestions(self, partial_tag: str, /) -> dict[str, typing.Any]:
2162    async def fetch_forum_tag_suggestions(
2163        self, partial_tag: str, /
2164    ) -> typedefs.JSONObject:
2165        resp = await self._request(
2166            RequestMethod.GET,
2167            "Forum/GetForumTagSuggestions/",
2168            json={"partialtag": partial_tag},
2169        )
2170        assert isinstance(resp, dict)
2171        return resp
async def fetch_poll(self, topic_id: int, /) -> dict[str, typing.Any]:
2173    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2174        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
2175        assert isinstance(resp, dict)
2176        return resp
async def fetch_recuirement_thread_summaries(self) -> list[typing.Any]:
2178    async def fetch_recuirement_thread_summaries(self) -> typedefs.JSONArray:
2179        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
2180        assert isinstance(resp, list)
2181        return resp
async def fetch_available_avatars(self) -> collections.abc.Mapping[str, int]:
2199    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2200        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2201        assert isinstance(resp, dict)
2202        return resp
async def fetch_user_clan_invite_setting( self, access_token: str, /, membership_type: Union[int, aiobungie.MembershipType]) -> bool:
2204    async def fetch_user_clan_invite_setting(
2205        self,
2206        access_token: str,
2207        /,
2208        membership_type: typedefs.IntAnd[enums.MembershipType],
2209    ) -> bool:
2210        resp = await self._request(
2211            RequestMethod.GET,
2212            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2213            auth=access_token,
2214        )
2215        assert isinstance(resp, bool)
2216        return resp
async def fetch_banned_group_members( self, access_token: str, group_id: int, /, *, page: int = 1) -> dict[str, typing.Any]:
2218    async def fetch_banned_group_members(
2219        self, access_token: str, group_id: int, /, *, page: int = 1
2220    ) -> typedefs.JSONObject:
2221        resp = await self._request(
2222            RequestMethod.GET,
2223            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2224            auth=access_token,
2225        )
2226        assert isinstance(resp, dict)
2227        return resp
async def fetch_pending_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> dict[str, typing.Any]:
2229    async def fetch_pending_group_memberships(
2230        self, access_token: str, group_id: int, /, *, current_page: int = 1
2231    ) -> typedefs.JSONObject:
2232        resp = await self._request(
2233            RequestMethod.GET,
2234            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2235            auth=access_token,
2236        )
2237        assert isinstance(resp, dict)
2238        return resp
async def fetch_invited_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> dict[str, typing.Any]:
2240    async def fetch_invited_group_memberships(
2241        self, access_token: str, group_id: int, /, *, current_page: int = 1
2242    ) -> typedefs.JSONObject:
2243        resp = await self._request(
2244            RequestMethod.GET,
2245            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2246            auth=access_token,
2247        )
2248        assert isinstance(resp, dict)
2249        return resp
async def invite_member_to_group( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], *, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
2251    async def invite_member_to_group(
2252        self,
2253        access_token: str,
2254        /,
2255        group_id: int,
2256        membership_id: int,
2257        membership_type: typedefs.IntAnd[enums.MembershipType],
2258        *,
2259        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2260    ) -> typedefs.JSONObject:
2261        resp = await self._request(
2262            RequestMethod.POST,
2263            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2264            auth=access_token,
2265            json={"message": str(message)},
2266        )
2267        assert isinstance(resp, dict)
2268        return resp
async def cancel_group_member_invite( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
2270    async def cancel_group_member_invite(
2271        self,
2272        access_token: str,
2273        /,
2274        group_id: int,
2275        membership_id: int,
2276        membership_type: typedefs.IntAnd[enums.MembershipType],
2277    ) -> typedefs.JSONObject:
2278        resp = await self._request(
2279            RequestMethod.POST,
2280            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2281            auth=access_token,
2282        )
2283        assert isinstance(resp, dict)
2284        return resp
async def fetch_historical_definition(self) -> dict[str, typing.Any]:
2286    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2287        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2288        assert isinstance(resp, dict)
2289        return resp
async def fetch_historical_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], day_start: datetime.datetime, day_end: datetime.datetime, groups: list[typing.Union[int, aiobungie.internal.enums.StatsGroupType]], modes: collections.abc.Sequence[typing.Union[int, aiobungie.GameMode]], *, period_type: aiobungie.internal.enums.PeriodType = <PeriodType.ALL_TIME: 2>) -> dict[str, typing.Any]:
2291    async def fetch_historical_stats(
2292        self,
2293        character_id: int,
2294        membership_id: int,
2295        membership_type: typedefs.IntAnd[enums.MembershipType],
2296        day_start: datetime.datetime,
2297        day_end: datetime.datetime,
2298        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2299        modes: collections.Sequence[typedefs.IntAnd[enums.GameMode]],
2300        *,
2301        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2302    ) -> typedefs.JSONObject:
2303
2304        end, start = time.parse_date_range(day_end, day_start)
2305        resp = await self._request(
2306            RequestMethod.GET,
2307            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2308            json={
2309                "dayend": end,
2310                "daystart": start,
2311                "groups": [str(int(group)) for group in groups],
2312                "modes": [str(int(mode)) for mode in modes],
2313                "periodType": int(period_type),
2314            },
2315        )
2316        assert isinstance(resp, dict)
2317        return resp

Fetch historical stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • day_start (datetime.datetime): The start of the day to return the stats for.
  • day_end (datetime.datetime): The end of the day to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
  • modes (list[aiobungie.GameMode | int]): A list of game modes to return.
  • period_type (aiobungie.enums.PeriodType): The period type to return the stats for. This will return ALL_TIME by default if not modified.
Returns
async def fetch_historical_stats_for_account( self, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], groups: list[typing.Union[int, aiobungie.internal.enums.StatsGroupType]]) -> dict[str, typing.Any]:
2319    async def fetch_historical_stats_for_account(
2320        self,
2321        membership_id: int,
2322        membership_type: typedefs.IntAnd[enums.MembershipType],
2323        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2324    ) -> typedefs.JSONObject:
2325        resp = await self._request(
2326            RequestMethod.GET,
2327            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2328            json={"groups": [str(int(group)) for group in groups]},
2329        )
2330        assert isinstance(resp, dict)
2331        return resp

Fetch historical stats for an account's membership.

Parameters
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], /) -> dict[str, typing.Any]:
2333    async def fetch_aggregated_activity_stats(
2334        self,
2335        character_id: int,
2336        membership_id: int,
2337        membership_type: typedefs.IntAnd[enums.MembershipType],
2338        /,
2339    ) -> typedefs.JSONObject:
2340        resp = await self._request(
2341            RequestMethod.GET,
2342            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2343            f"Character/{character_id}/Stats/AggregateActivityStats/",
2344        )
2345        assert isinstance(resp, dict)
2346        return resp

Fetch aggregated activity stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
Returns
class RESTPool:
236class RESTPool:
237    """Pool of `RESTClient` instances.
238
239    This allows to create multiple instances of `RESTClient`s that can be acquired
240    which share the same config and metadata.
241
242    Example
243    -------
244    ```py
245    import aiobungie
246    import asyncio
247
248    client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')
249
250    # Using a context manager to acquire an instance
251    # of the pool and close the connection after finishing.
252
253    async def first() -> str:
254        async with client_pool.acquire() as client:
255            return client.build_oauth2_url()
256
257    async def second() -> None:
258        async with client_pool.acquire() as client:
259            new_tokens = await client.refresh_access_token("token")
260            client.metadata['tokens'] = new_tokens
261
262    # Client instances are independent from first and second.
263    await asyncio.gather(first(), second())
264    ```
265
266    Parameters
267    ----------
268    token : `str`
269        A valid application token from Bungie's developer portal.
270
271    Other Parameters
272    ----------------
273    max_retries : `int`
274        The max retries number to retry if the request hit a `5xx` status code.
275    max_ratelimit_retries : `int`
276        The max retries number to retry if the request hit a `429` status code. Defaults to `3`.
277    client_secret : `typing.Optional[str]`
278        An optional application client secret,
279        This is only needed if you're fetching OAuth2 tokens with this client.
280    client_id : `typing.Optional[int]`
281        An optional application client id,
282        This is only needed if you're fetching OAuth2 tokens with this client.
283    enable_debugging : `bool | str`
284        Whether to enable logging responses or not.
285
286    Logging Levels
287    --------------
288    * `False`: This will disable logging.
289    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
290    Like the response status, route, taken time and so on.
291    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
292    """
293
294    __slots__ = (
295        "_token",
296        "_max_retries",
297        "_client_secret",
298        "_client_id",
299        "_max_rate_limit_retries",
300        "_metadata",
301        "_enable_debug",
302    )
303
304    # Looks like mypy doesn't like this.
305    if typing.TYPE_CHECKING:
306        _enable_debug: typing.Union[typing.Literal["TRACE"], bool, int]
307
308    def __init__(
309        self,
310        token: str,
311        /,
312        client_secret: typing.Optional[str] = None,
313        client_id: typing.Optional[int] = None,
314        *,
315        max_retries: int = 4,
316        max_rate_limit_retries: int = 3,
317        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
318    ) -> None:
319        self._client_secret = client_secret
320        self._client_id = client_id
321        self._token: str = token
322        self._max_retries = max_retries
323        self._max_rate_limit_retries = max_rate_limit_retries
324        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
325        self._enable_debug = enable_debugging
326
327    @property
328    def client_id(self) -> typing.Optional[int]:
329        return self._client_id
330
331    @property
332    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
333        """Pool's Metadata. This is different from client instance metadata."""
334        return self._metadata
335
336    @typing.final
337    def acquire(self) -> RESTClient:
338        """Acquires a new `RESTClient` instance from this REST pool.
339
340        Returns
341        -------
342        `RESTClient`
343            An instance of a REST client.
344        """
345        instance = RESTClient(
346            self._token,
347            client_secret=self._client_secret,
348            client_id=self._client_id,
349            max_retries=self._max_retries,
350            max_ratelimit_retries=self._max_rate_limit_retries,
351            enable_debugging=self._enable_debug,
352        )
353        return instance

Pool of RESTClient instances.

This allows to create multiple instances of RESTClients that can be acquired which share the same config and metadata.

Example
import aiobungie
import asyncio

client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')

# Using a context manager to acquire an instance
# of the pool and close the connection after finishing.

async def first() -> str:
    async with client_pool.acquire() as client:
        return client.build_oauth2_url()

async def second() -> None:
    async with client_pool.acquire() as client:
        new_tokens = await client.refresh_access_token("token")
        client.metadata['tokens'] = new_tokens

# Client instances are independent from first and second.
await asyncio.gather(first(), second())
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • max_ratelimit_retries (int): The max retries number to retry if the request hit a 429 status code. Defaults to 3.
  • client_secret (typing.Optional[str]): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (typing.Optional[int]): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information. Like the response status, route, taken time and so on.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
RESTPool( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, max_retries: int = 4, max_rate_limit_retries: int = 3, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
308    def __init__(
309        self,
310        token: str,
311        /,
312        client_secret: typing.Optional[str] = None,
313        client_id: typing.Optional[int] = None,
314        *,
315        max_retries: int = 4,
316        max_rate_limit_retries: int = 3,
317        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
318    ) -> None:
319        self._client_secret = client_secret
320        self._client_id = client_id
321        self._token: str = token
322        self._max_retries = max_retries
323        self._max_rate_limit_retries = max_rate_limit_retries
324        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
325        self._enable_debug = enable_debugging
metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

Pool's Metadata. This is different from client instance metadata.

@typing.final
def acquire(self) -> aiobungie.RESTClient:
336    @typing.final
337    def acquire(self) -> RESTClient:
338        """Acquires a new `RESTClient` instance from this REST pool.
339
340        Returns
341        -------
342        `RESTClient`
343            An instance of a REST client.
344        """
345        instance = RESTClient(
346            self._token,
347            client_secret=self._client_secret,
348            client_id=self._client_id,
349            max_retries=self._max_retries,
350            max_ratelimit_retries=self._max_rate_limit_retries,
351            enable_debugging=self._enable_debug,
352        )
353        return instance

Acquires a new RESTClient instance from this REST pool.

Returns
@typing.final
class Race(builtins.int, aiobungie.Enum):
493@typing.final
494class Race(int, Enum):
495    """An Enum for Destiny races."""
496
497    HUMAN = 0
498    AWOKEN = 1
499    EXO = 2
500    UNKNOWN = 3

An Enum for Destiny races.

HUMAN = <Race.HUMAN: 0>
AWOKEN = <Race.AWOKEN: 1>
EXO = <Race.EXO: 2>
UNKNOWN = <Race.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Raid(builtins.int, aiobungie.Enum):
143@typing.final
144class Raid(int, Enum):
145    """An Enum for all available raids in Destiny 2."""
146
147    DSC = 910380154
148    """Deep Stone Crypt"""
149
150    LW = 2122313384
151    """Last Wish"""
152
153    VOG = 3881495763
154    """Normal Valut of Glass"""
155
156    GOS = 3458480158
157    """Garden Of Salvation"""

An Enum for all available raids in Destiny 2.

DSC = <Raid.DSC: 910380154>

Deep Stone Crypt

LW = <Raid.LW: 2122313384>

Last Wish

VOG = <Raid.VOG: 3881495763>

Normal Valut of Glass

GOS = <Raid.GOS: 3458480158>

Garden Of Salvation

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class RateLimitedError(aiobungie.HTTPError):
253@attrs.define(auto_exc=True)
254class RateLimitedError(HTTPError):
255    """Raised when too many request status code is returned."""
256
257    http_status: http.HTTPStatus = attrs.field(
258        default=http.HTTPStatus.TOO_MANY_REQUESTS, init=False
259    )
260    """The request response http status."""
261
262    url: typedefs.StrOrURL
263    """The URL/endpoint caused this error."""
264
265    body: typing.Any
266    """The response body."""
267
268    retry_after: float = attrs.field(default=0.0)
269    """The amount of seconds you need to wait before retrying to requests."""
270
271    message: str = attrs.field(init=False)
272    """A Bungie human readable message describes the cause of the error."""
273
274    @message.default  # type: ignore
275    def _(self) -> str:
276        return f"You're ratelimited for {self.retry_after}, Endpoint: {self.url}. Slow down!"
277
278    def __str__(self) -> str:
279        return self.message

Raised when too many request status code is returned.

RateLimitedError(url: Union[str, yarl.URL], body: Any, retry_after: float = 0.0)
2def __init__(self, url, body, retry_after=attr_dict['retry_after'].default):
3    self.http_status = attr_dict['http_status'].default
4    self.url = url
5    self.body = body
6    self.retry_after = retry_after
7    self.message = __attr_factory_message(self)
8    BaseException.__init__(self, self.url,self.body,self.retry_after)

Method generated by attrs for class RateLimitedError.

http_status: http.HTTPStatus

The request response http status.

url: Union[str, yarl.URL]

The URL/endpoint caused this error.

body: Any

The response body.

retry_after: float

The amount of seconds you need to wait before retrying to requests.

message: str

A Bungie human readable message describes the cause of the error.

Inherited Members
builtins.BaseException
with_traceback
@typing.final
class RecordState(aiobungie.Flag):
48@typing.final
49class RecordState(enums.Flag):
50    """An enum for records component states."""
51
52    NONE = 0
53    REDEEMED = 1 << 0
54    UNAVAILABLE = 1 << 1
55    OBJECTIVE_NOT_COMPLETED = 1 << 2
56    OBSCURED = 1 << 3
57    INVISIBLE = 1 << 4
58    ENTITLEMENT_UNOWNED = 1 << 5
59    CAN_EQUIP_TITLE = 1 << 6

An enum for records component states.

NONE = <RecordState.NONE: 0>
REDEEMED = <RecordState.REDEEMED: 1>
UNAVAILABLE = <RecordState.UNAVAILABLE: 2>
OBJECTIVE_NOT_COMPLETED = <RecordState.OBJECTIVE_NOT_COMPLETED: 4>
OBSCURED = <RecordState.OBSCURED: 8>
INVISIBLE = <RecordState.INVISIBLE: 16>
ENTITLEMENT_UNOWNED = <RecordState.ENTITLEMENT_UNOWNED: 32>
CAN_EQUIP_TITLE = <RecordState.CAN_EQUIP_TITLE: 64>
Inherited Members
Flag
name
value
@typing.final
class Relationship(builtins.int, aiobungie.Enum):
688@typing.final
689class Relationship(int, Enum):
690    """An enum for bungie friends relationship types."""
691
692    UNKNOWN = 0
693    FRIEND = 1
694    INCOMING_REQUEST = 2
695    OUTGOING_REQUEST = 3

An enum for bungie friends relationship types.

UNKNOWN = <Relationship.UNKNOWN: 0>
FRIEND = <Relationship.FRIEND: 1>
INCOMING_REQUEST = <Relationship.INCOMING_REQUEST: 2>
OUTGOING_REQUEST = <Relationship.OUTGOING_REQUEST: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RequestMethod(builtins.str, aiobungie.Enum):
217class RequestMethod(str, enums.Enum):
218    """HTTP request methods enum."""
219
220    GET = "GET"
221    """GET methods."""
222    POST = "POST"
223    """POST methods."""
224    PUT = "PUT"
225    """PUT methods."""
226    PATCH = "PATCH"
227    """PATCH methods."""
228    DELETE = "DELETE"
229    """DELETE methods"""

HTTP request methods enum.

GET = <RequestMethod.GET: GET>

GET methods.

POST = <RequestMethod.POST: POST>

POST methods.

PUT = <RequestMethod.PUT: PUT>

PUT methods.

PATCH = <RequestMethod.PATCH: PATCH>

PATCH methods.

DELETE = <RequestMethod.DELETE: DELETE>

DELETE methods

Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@attrs.define(auto_exc=True)
class ResponseError(aiobungie.HTTPException):
248@attrs.define(auto_exc=True)
249class ResponseError(HTTPException):
250    """Exception for other HTTP response errors."""

Exception for other HTTP response errors.

ResponseError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class ResponseError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class Stat(builtins.int, aiobungie.Enum):
515@typing.final
516class Stat(int, Enum):
517    """An Enum for Destiny 2 character stats."""
518
519    NONE = 0
520    MOBILITY = 2996146975
521    RESILIENCE = 392767087
522    RECOVERY = 1943323491
523    DISCIPLINE = 1735777505
524    INTELLECT = 144602215
525    STRENGTH = 4244567218
526    LIGHT_POWER = 1935470627

An Enum for Destiny 2 character stats.

NONE = <Stat.NONE: 0>
MOBILITY = <Stat.MOBILITY: 2996146975>
RESILIENCE = <Stat.RESILIENCE: 392767087>
RECOVERY = <Stat.RECOVERY: 1943323491>
DISCIPLINE = <Stat.DISCIPLINE: 1735777505>
INTELLECT = <Stat.INTELLECT: 144602215>
STRENGTH = <Stat.STRENGTH: 4244567218>
LIGHT_POWER = <Stat.LIGHT_POWER: 1935470627>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
TRACE = 5
@typing.final
class TierType(builtins.int, aiobungie.Enum):
630@typing.final
631class TierType(int, Enum):
632    """An enum for a Destiny 2 item tier type."""
633
634    UNKNOWN = 0
635    CURRENCY = 1
636    BASIC = 2
637    COMMON = 3
638    RARE = 4
639    SUPERIOR = 5
640    EXOTIC = 6

An enum for a Destiny 2 item tier type.

UNKNOWN = <TierType.UNKNOWN: 0>
CURRENCY = <TierType.CURRENCY: 1>
BASIC = <TierType.BASIC: 2>
COMMON = <TierType.COMMON: 3>
RARE = <TierType.RARE: 4>
SUPERIOR = <TierType.SUPERIOR: 5>
EXOTIC = <TierType.EXOTIC: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class TransferStatus(aiobungie.Flag):
740@typing.final
741class TransferStatus(Flag):
742    """An enum for items transfer statuses."""
743
744    CAN_TRANSFER = 0
745    """The item can be transferred."""
746    IS_EQUIPPED = 1 << 0
747    """You can't transfer since the item is equipped."""
748    NOT_TRASNFERRABLE = 1 << 1
749    """This item can not be transferred."""
750    COULD_BE_TRANSFERRED = 1 << 2
751    """You can trasnfer the item. But the place you're trying to put it at has no space for it."""

An enum for items transfer statuses.

CAN_TRANSFER = <TransferStatus.CAN_TRANSFER: 0>

The item can be transferred.

IS_EQUIPPED = <TransferStatus.IS_EQUIPPED: 1>

You can't transfer since the item is equipped.

NOT_TRASNFERRABLE = <TransferStatus.NOT_TRASNFERRABLE: 2>

This item can not be transferred.

COULD_BE_TRANSFERRED = <TransferStatus.COULD_BE_TRANSFERRED: 4>

You can trasnfer the item. But the place you're trying to put it at has no space for it.

Inherited Members
Flag
name
value
UNDEFINED = UNDEFINED
@attrs.define(auto_exc=True)
class Unauthorized(aiobungie.HTTPException):
155@attrs.define(auto_exc=True)
156class Unauthorized(HTTPException):
157    """An exception that's raised when trying to make unauthorized call to a resource and it returns 404."""
158
159    http_status: http.HTTPStatus = attrs.field(
160        default=http.HTTPStatus.UNAUTHORIZED, init=False
161    )

An exception that's raised when trying to make unauthorized call to a resource and it returns 404.

Unauthorized( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Unauthorized.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
UndefinedOr = typing.Union[aiobungie.UndefinedType, +_T]
class UndefinedType:
33class UndefinedType:
34    """An `UNDEFINED` type."""
35
36    __instance: typing.Optional[UndefinedType] = None
37
38    def __bool__(self) -> typing.Literal[False]:
39        return False
40
41    def __int__(self) -> typing.Literal[0]:
42        return 0
43
44    def __repr__(self) -> str:
45        return "UNDEFINED"
46
47    def __str__(self) -> str:
48        return "UNDEFINED"
49
50    def __new__(cls) -> UndefinedType:
51        if cls.__instance is None:
52            o = super().__new__(cls)
53            cls.__instance = o
54        return cls.__instance

An UNDEFINED type.

UndefinedType()
@typing.final
class ValueUIStyle(builtins.int, aiobungie.Enum):
75@typing.final
76class ValueUIStyle(int, enums.Enum):
77    AUTOMATIC = 0
78    FRACTION = 1
79    CHECK_BOX = 2
80    PERCENTAGE = 3
81    DATETIME = 4
82    FRACTION_FLOAT = 5
83    INTEGER = 6
84    TIME_DURATION = 7
85    HIDDEN = 8
86    MULTIPLIER = 9
87    GREEN_PIPS = 10
88    RED_PIPS = 11
89    EXPLICIT_PERCENTAGE = 12
90    RAW_FLOAT = 13
91    LEVEL_AND_REWARD = 14

An enumeration.

AUTOMATIC = <ValueUIStyle.AUTOMATIC: 0>
FRACTION = <ValueUIStyle.FRACTION: 1>
CHECK_BOX = <ValueUIStyle.CHECK_BOX: 2>
PERCENTAGE = <ValueUIStyle.PERCENTAGE: 3>
DATETIME = <ValueUIStyle.DATETIME: 4>
FRACTION_FLOAT = <ValueUIStyle.FRACTION_FLOAT: 5>
INTEGER = <ValueUIStyle.INTEGER: 6>
TIME_DURATION = <ValueUIStyle.TIME_DURATION: 7>
HIDDEN = <ValueUIStyle.HIDDEN: 8>
MULTIPLIER = <ValueUIStyle.MULTIPLIER: 9>
GREEN_PIPS = <ValueUIStyle.GREEN_PIPS: 10>
RED_PIPS = <ValueUIStyle.RED_PIPS: 11>
EXPLICIT_PERCENTAGE = <ValueUIStyle.EXPLICIT_PERCENTAGE: 12>
RAW_FLOAT = <ValueUIStyle.RAW_FLOAT: 13>
LEVEL_AND_REWARD = <ValueUIStyle.LEVEL_AND_REWARD: 14>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Vendor(builtins.int, aiobungie.Enum):
240@typing.final
241class Vendor(int, Enum):
242    """An Enum for all available vendors in Destiny 2."""
243
244    ZAVALA = 69482069
245    XUR = 2190858386
246    BANSHE = 672118013
247    SPIDER = 863940356
248    SHAXX = 3603221665
249    KADI = 529635856
250    """Postmaster exo."""
251    YUNA = 1796504621
252    """Asia servers only."""
253    EVERVERSE = 3361454721
254    AMANDA = 460529231
255    """Amanda holiday"""
256    CROW = 3611983588
257    HAWTHORNE = 3347378076
258    ADA1 = 350061650
259    DRIFTER = 248695599
260    IKORA = 1976548992
261    SAINT = 765357505
262    """Saint-14"""
263    ERIS_MORN = 1616085565
264    SHAW_HAWN = 1816541247
265    """COSMODROME Guy"""
266    VARIKS = 2531198101

An Enum for all available vendors in Destiny 2.

ZAVALA = <Vendor.ZAVALA: 69482069>
XUR = <Vendor.XUR: 2190858386>
BANSHE = <Vendor.BANSHE: 672118013>
SPIDER = <Vendor.SPIDER: 863940356>
SHAXX = <Vendor.SHAXX: 3603221665>
KADI = <Vendor.KADI: 529635856>

Postmaster exo.

YUNA = <Vendor.YUNA: 1796504621>

Asia servers only.

EVERVERSE = <Vendor.EVERVERSE: 3361454721>
AMANDA = <Vendor.AMANDA: 460529231>

Amanda holiday

CROW = <Vendor.CROW: 3611983588>
HAWTHORNE = <Vendor.HAWTHORNE: 3347378076>
ADA1 = <Vendor.ADA1: 350061650>
DRIFTER = <Vendor.DRIFTER: 248695599>
IKORA = <Vendor.IKORA: 1976548992>
SAINT = <Vendor.SAINT: 765357505>

Saint-14

ERIS_MORN = <Vendor.ERIS_MORN: 1616085565>
SHAW_HAWN = <Vendor.SHAW_HAWN: 1816541247>

COSMODROME Guy

VARIKS = <Vendor.VARIKS: 2531198101>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class WeaponType(builtins.int, aiobungie.Enum):
529@typing.final
530class WeaponType(int, Enum):
531    """Enums for The three Destiny Weapon Types"""
532
533    NONE = 0
534    KINETIC = 1498876634
535    ENERGY = 2465295065
536    POWER = 953998645

Enums for The three Destiny Weapon Types

NONE = <WeaponType.NONE: 0>
KINETIC = <WeaponType.KINETIC: 1498876634>
ENERGY = <WeaponType.ENERGY: 2465295065>
POWER = <WeaponType.POWER: 953998645>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
annotations = _Feature((3, 7, 0, 'beta', 1), (3, 11, 0, 'alpha', 0), 16777216)
def into_iter( iterable: collections.abc.Iterable[~Item]) -> aiobungie.Iterator[~Item]:
559def into_iter(
560    iterable: collections.Iterable[Item],
561) -> Iterator[Item]:
562    """Transform an iterable into an flat iterator.
563
564    Example
565    -------
566    ```py
567    sequence = [1,2,3]
568    for item in aiobungie.into_iter(sequence).reversed():
569        print(item)
570    # 3
571    # 2
572    # 1
573    ```
574
575    Parameters
576    ----------
577    iterable: `typing.Iterable[Item]`
578        The iterable to convert.
579
580    Raises
581    ------
582    `StopIteration`
583        If no elements are left in the iterator.
584    """
585    return Iterator(iterable)

Transform an iterable into an flat iterator.

Example
sequence = [1,2,3]
for item in aiobungie.into_iter(sequence).reversed():
    print(item)
# 3
# 2
# 1
Parameters
  • iterable (typing.Iterable[Item]): The iterable to convert.
Raises
  • StopIteration: If no elements are left in the iterator.
async def raise_error( response: aiohttp.client_reqrep.ClientResponse) -> aiobungie.AiobungieError:
282async def raise_error(response: aiohttp.ClientResponse) -> AiobungieError:
283    """Generates and raise exceptions on error responses."""
284
285    # Not a JSON response, raise immediately.
286
287    # Also Bungie sometimes get funky and return HTML instead of JSON when making an authorized
288    # request with a dummy access token. I can't really do anything about this..
289    if response.content_type != "application/json":
290        return HTTPError(
291            f"Expected JSON content but got {response.content_type!s}, {response.real_url!s}",
292            http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE,
293        )
294
295    body = await response.json()
296    message: str = body.get("Message", "UNDEFINED_MESSAGE")
297    error_status: str = body.get("ErrorStatus", "UNDEFINED_ERROR_STATUS")
298    message_data: dict[str, str] = body.get("MessageData", {})
299    throttle_seconds: int = body.get("ThrottleSeconds", 0)
300    error_code: int = body.get("ErrorCode", 0)
301
302    # Standard HTTP status.
303    if response.status == http.HTTPStatus.NOT_FOUND:
304        return NotFound(
305            message=message,
306            error_code=error_code,
307            throttle_seconds=throttle_seconds,
308            url=str(response.real_url),
309            body=body,
310            headers=response.headers,
311            error_status=error_status,
312            message_data=message_data,
313        )
314
315    elif response.status == http.HTTPStatus.FORBIDDEN:
316        return Forbidden(
317            message=message,
318            error_code=error_code,
319            throttle_seconds=throttle_seconds,
320            url=str(response.real_url),
321            body=body,
322            headers=response.headers,
323            error_status=error_status,
324            message_data=message_data,
325        )
326
327    elif response.status == http.HTTPStatus.UNAUTHORIZED:
328        return Unauthorized(
329            message=message,
330            error_code=error_code,
331            throttle_seconds=throttle_seconds,
332            url=str(response.real_url),
333            body=body,
334            headers=response.headers,
335            error_status=error_status,
336            message_data=message_data,
337        )
338
339    elif response.status == http.HTTPStatus.BAD_REQUEST:
340        # Membership needs to be alone.
341        if error_status == "InvalidParameters":
342            return MembershipTypeError(
343                message=message,
344                body=body,
345                headers=response.headers,
346                url=str(response.url),
347                membership_type=message_data["membershipType"],
348                required_membership=message_data["membershipInfo.membershipType"],
349                membership_id=int(message_data["membershipId"]),
350            )
351        return BadRequest(
352            message=message,
353            body=body,
354            headers=response.headers,
355            url=str(response.url),
356        )
357
358    status = http.HTTPStatus(response.status)
359
360    if 400 <= status < 500:
361        return ResponseError(
362            message=message,
363            error_code=error_code,
364            throttle_seconds=throttle_seconds,
365            url=str(response.real_url),
366            body=body,
367            headers=response.headers,
368            error_status=error_status,
369            message_data=message_data,
370            http_status=status,
371        )
372
373    # Need to self handle ~5xx errors
374    elif 500 <= status < 600:
375        # No API key or method requires OAuth2 most likely.
376        if error_status in {
377            "ApiKeyMissingFromRequest",
378            "WebAuthRequired",
379            "ApiInvalidOrExpiredKey",
380            "AuthenticationInvalid",
381            "AuthorizationCodeInvalid",
382        }:
383            return Unauthorized(
384                message=message,
385                error_code=error_code,
386                throttle_seconds=throttle_seconds,
387                url=str(response.real_url),
388                body=body,
389                headers=response.headers,
390                error_status=error_status,
391                message_data=message_data,
392            )
393
394        # Anything contains not found.
395        elif (
396            "NotFound" in error_status or error_status == "UserCannotFindRequestedUser"
397        ):
398            return NotFound(
399                message=message,
400                error_code=error_code,
401                throttle_seconds=throttle_seconds,
402                url=str(response.real_url),
403                body=body,
404                headers=response.headers,
405                error_status=error_status,
406                message_data=message_data,
407            )
408
409        # Other 5xx errors.
410        else:
411            return InternalServerError(
412                message=message,
413                error_code=error_code,
414                throttle_seconds=throttle_seconds,
415                url=str(response.real_url),
416                body=body,
417                headers=response.headers,
418                error_status=error_status,
419                message_data=message_data,
420                http_status=status,
421            )
422    # Something else.
423    else:
424        return HTTPException(
425            message=message,
426            error_code=error_code,
427            throttle_seconds=throttle_seconds,
428            url=str(response.real_url),
429            body=body,
430            headers=response.headers,
431            error_status=error_status,
432            message_data=message_data,
433            http_status=status,
434        )

Generates and raise exceptions on error responses.

def stringify_http_message(headers: 'collections.Mapping[str, str]') -> str:
437def stringify_http_message(headers: collections.Mapping[str, str]) -> str:
438    return (
439        "{ \n"
440        + "\n".join(  # noqa: W503
441            f"{f'   {key}'}: {value}"
442            if key not in ("Authorization", "X-API-KEY")
443            else f"   {key}: HIDDEN_TOKEN"
444            for key, value in headers.items()
445        )
446        + "\n}"  # noqa: W503
447    )